Appearance
Webhook setup
Shiphook is triggered by an HTTP POST to its URL. You configure your Git host (or any service) to send that POST when you push. No special payload format is required — Shiphook only cares that the request is POST and that a secret is sent.
In multi-app mode (apps:), Shiphook first matches app route by Host + path and then validates that matched app's secret. Keys are isolated per app/repo. If an app secret is omitted in config, Shiphook generates and persists one for that app on startup.
GitHub requires a public HTTPS URL. Put nginx (or another reverse proxy) and TLS in front of Shiphook; see HTTPS (nginx + Certbot).
What Shiphook expects
- Method:
POST - URL: Your Shiphook base URL + path (e.g.
http://your-server:3141/orhttp://your-server:3141/webhookif you setpath: /webhookin shiphook.yaml orSHIPHOOK_PATH=/webhook) - Auth (required): Include the secret in one of:
- Header:
X-Shiphook-Secret: <secret> - Header:
Authorization: Bearer <secret>
- Header:
Shiphook does not validate payload content (e.g. GitHub payload). It runs git pull and the run script on every authorized POST.
Multi-app routing/auth example
yaml
apps:
- name: app-a
host: app-a.example.com
path: /deploy
repoPath: /srv/app-a
runScript: npm run deploy
secret: app-a-secret
- name: app-b
host: app-b.example.com
path: /deploy
repoPath: /srv/app-b
runScript: npm run deploy
secret: app-b-secretWith this config:
https://app-a.example.com/deployonly acceptsapp-a-secrethttps://app-b.example.com/deployonly acceptsapp-b-secretapp-a-secretcannot deploy app B (and vice versa)
Logs
For each authorized POST, Shiphook runs the deploy and writes logs into .shiphook/logs/:
.shiphook/logs/<UTC-date>_<id>.json.shiphook/logs/<UTC-date>_<id>.log
The filename stem is YYYY-MM-DD_HH-mm-ssZ (UTC) plus _ and a UUID so listings sort by time and stay unique.
By default, Shiphook streams deploy output back to the HTTP client (plain text) and finishes with a final line like:
[done] ok=true exitCode=0
If you need the old buffered JSON response, use ?format=json (the JSON includes log: { id, json, log }).
GitHub
- Open your repo → Settings → Webhooks → Add webhook.
- Payload URL: Your public HTTPS URL (e.g.
https://deploy.example.com/orhttps://deploy.example.com/webhook). Use HTTPS setup; Shiphook usually stays onhttp://127.0.0.1:3141behind nginx. - Content type:
application/json. - Secret: Use the same value as
SHIPHOOK_SECRET(or the value from.shiphook.secret) so only GitHub can trigger the deploy. - Which events: “Just the push event” is enough.
- Save.
On every push, GitHub sends a POST to that URL. Shiphook runs git pull and your run script.
GitHub Actions (live streaming)
When Shiphook receives the webhook, it streams git pull + your deploy script output back to the HTTP response.
To see it live in the GitHub Actions step log:
- Trigger Shiphook with
curl -N(no curl buffering). - Optionally capture the output and check the final
[done] ...line so the job fails if deploy fails.
Minimal shiphook.yaml (server-side config):
yaml
secret: your-secret
runScript: npm run deployMinimal GitHub Actions workflow example (.github/workflows/deploy.yml):
yaml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Trigger Shiphook webhook (streaming)
env:
SHIPHOOK_SECRET: ${{ secrets.SHIPHOOK_SECRET }}
SHIPHOOK_URL: ${{ secrets.SHIPHOOK_URL }} # optional (defaults to https://shiphook.majico.xyz)
run: |
BASE="${SHIPHOOK_URL:-https://shiphook.majico.xyz}"
BASE="${BASE%/}"
LOG_FILE="$(mktemp)"
curl -N -S -X POST "${BASE}/" \
-H "X-Shiphook-Secret: ${SHIPHOOK_SECRET}" \
-H "Content-Type: application/json" \
-d '{}' 2>&1 | tee "${LOG_FILE}"
DONE_LINE="$(tail -n 1 "${LOG_FILE}")"
echo "Final line: ${DONE_LINE}"
echo "${DONE_LINE}" | grep -q '\[done\] ok=true' || exit 1GitLab
- Open your repo → Settings → Webhooks.
- URL: Your Shiphook URL.
- Secret token: Same value as
SHIPHOOK_SECRET(or the value from.shiphook.secret). - Trigger: Push events.
- Add webhook.
Generic: any HTTP client
Any service that can send an HTTP POST works. Example with curl:
bash
curl -X POST http://your-server:3141/ \
-H "X-Shiphook-Secret: your-secret"You can send the secret via either header:
bash
# Header: X-Shiphook-Secret
curl -X POST http://your-server:3141/ \
-H "X-Shiphook-Secret: your-secret"Or:
bash
# Header: Authorization: Bearer <secret>
curl -X POST http://your-server:3141/ \
-H "Authorization: Bearer your-secret"What Shiphook returns
Shiphook returns:
- HTTP
200for valid authenticated requests (valid secret) - HTTP
401for missing/invalid secrets
For successful requests (HTTP 200):
- Default: streaming plain text output ending with
[done] ok=... exitCode=... - Opt-out JSON: use
?format=jsonfor the old buffered JSON response
For HTTP 401, the response is still JSON.
Success JSON (?format=json, run script exited 0):
json
{
"ok": true,
"pull": {
"success": true,
"stdout": "...",
"stderr": ""
},
"run": {
"stdout": "...",
"stderr": "",
"exitCode": 0
},
"error": null
}Failure JSON (?format=json, run script exited non-zero):
json
{
"ok": false,
"pull": { "success": true, "stdout": "...", "stderr": "" },
"run": {
"stdout": "...",
"stderr": "...",
"exitCode": 1
},
"error": null
}ok:trueonly when the run script exit code is0.pull.success:trueifgit pullcompleted without throwing (e.g. no remote is still a “success” for pull; run script still runs).run.exitCode: exit code of the run script, ornullif the process could not be started.error: set if something went wrong (e.g. pull threw, or run process failed to start).
Use this response for monitoring or alerting (e.g. from GitHub Actions, Uptime Robot, or your own scripts). Non-200 status codes: 404 for wrong method/path, 401 when a secret is required and missing or wrong.