https://github.com/cap-jmk-real/shiphook
Ship on hook. Webhook → git pull → run your deploy script. Configure via shiphook.yaml or env. Self-hosted ideal for indie devs and micro-SaaS.
https://github.com/cap-jmk-real/shiphook
automation deployment developer-tools self-hosted webhook
Last synced: 3 months ago
JSON representation
Ship on hook. Webhook → git pull → run your deploy script. Configure via shiphook.yaml or env. Self-hosted ideal for indie devs and micro-SaaS.
- Host: GitHub
- URL: https://github.com/cap-jmk-real/shiphook
- Owner: cap-jmk-real
- License: mit
- Created: 2026-02-28T11:56:12.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-03-18T18:42:40.000Z (3 months ago)
- Last Synced: 2026-03-19T07:14:24.747Z (3 months ago)
- Topics: automation, deployment, developer-tools, self-hosted, webhook
- Language: TypeScript
- Homepage: https://cap-jmk-real.github.io/shiphook/
- Size: 168 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Shiphook
**Self-hosted deploys from Git webhooks** — receive a signed POST, run `git pull`, then run your deploy command. No third-party deploy product: your server, your repo, your script.
Shiphook is aimed at **indie projects**, **small SaaS**, and **open source** teams who want something simple they can read and own.
[](https://github.com/cap-jmk-real/shiphook/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/shiphook)
[](https://opensource.org/licenses/MIT)
[](https://www.typescriptlang.org/)
[](https://nodejs.org/)
[](https://cap-jmk-real.github.io/shiphook/)
[](https://coderabbit.ai)
---
## What it does
1. You run the Shiphook HTTP server in (or next to) your app repo.
2. Your Git host sends a webhook when you push.
3. Shiphook routes the request by host/path, verifies the matched app secret, runs **`git pull`**, reloads **`shiphook.yaml` from the repo when it lives in that tree**, and runs your **`runScript`** (build, restart containers, etc.).
4. Output can stream back in the HTTP response (useful for GitHub Actions logs) or as JSON (`?format=json`).
Configuration is **`shiphook.yaml`** in the repo and/or **environment variables** (env wins on conflicts).
---
## Install
```bash
npm install -g shiphook
```
Requires **Node 22+**.
---
## Run the server
```bash
cd /path/to/your/repo
shiphook
```
Default listen port: **3141**. Trigger a deploy:
```bash
curl -X POST http://localhost:3141/
```
Send the webhook secret as **`X-Shiphook-Secret`** or **`Authorization: Bearer …`** (see [Configuration](#configuration-yaml-or-environment)).
---
## Manual deploy (no webhook)
```bash
shiphook deploy
```
Same flow as a webhook: `git pull`, then your script.
---
## CLI
| Command | Purpose |
|--------|---------|
| `shiphook` | Start the server (or systemd integration on Linux — see docs). |
| `shiphook deploy` | Run one deploy in the foreground. |
| `shiphook cleanup --domain \| --all` | Linux cleanup helper for Shiphook nginx/systemd state (with backup). |
| `shiphook version` | Print version (`-v` / `--version` also work). |
| `shiphook setup-https` | Linux helper for nginx + Let’s Encrypt (GitHub needs HTTPS). |
---
## Logs
Each deploy writes files under **`.shiphook/logs/`**:
- **`_.json`** — structured log for tools.
- **`_.log`** — human-readable.
With **`?format=json`**, the HTTP body includes `log: { id, json, log }` so you can open the matching files.
---
## HTTPS (GitHub and most hosts)
Hosts expect a **public HTTPS** URL. Shiphook speaks HTTP on localhost; put **nginx** (or similar) and **Let’s Encrypt** in front.
On **Linux**, run **`shiphook setup-https`** or say **`y`** the first time you start `shiphook` in a TTY — the installer can install packages, configure nginx, obtain certs, and install a **systemd** unit. Details: [HTTPS setup](https://cap-jmk-real.github.io/shiphook/self-hosted-https.html).
For servers without a TTY, set **`SHIPHOOK_SKIP_HTTPS_PROMPT=1`**.
---
## Multi-app on one server
Shiphook supports two deployment models on one host:
- **Single process, multi-app (recommended):** one `shiphook` process with one `shiphook.yaml` that uses `apps:` and routes by `host + path`.
- **Per-app process:** one repo + service per app/domain (often one local port per app).
The first model is usually easier to operate in production. The second model is useful while iterating on individual app pipelines.
---
## Cleanup during pipeline development
While iterating on webhook/CD setup, it is common to accumulate stale nginx/server blocks or old systemd units. Use the built-in cleanup command before re-running setup:
```bash
# remove configs for one webhook domain (matching nginx files and systemd units)
shiphook cleanup --domain shiphook.example.com
# or remove all Shiphook-managed nginx/systemd entries
shiphook cleanup --all
```
The cleanup command creates a timestamped nginx backup before applying changes.
---
## Configuration (YAML or environment)
Add **`shiphook.yaml`** (see [shiphook.example.yaml](shiphook.example.yaml)) or use env vars. **Env overrides the file.**
| Option | Default | Notes |
|--------|---------|--------|
| `port` / `SHIPHOOK_PORT` | `3141` | Listen port. |
| `repoPath` / `SHIPHOOK_REPO_PATH` | current directory | Where `git pull` and the script run. |
| `runScript` / `SHIPHOOK_RUN_SCRIPT` | `npm run deploy` | Command after pull. |
| `secret` / `SHIPHOOK_SECRET` | (generated) | Required. Omit in YAML and the CLI can create **`.shiphook.secret`**. |
| `path` / `SHIPHOOK_PATH` | `/` | URL path for the webhook (e.g. `/deploy`). |
After **`git pull`**, Shiphook reloads **repo-local** YAML when the config file lives **inside** the repo. Paths set with **`SHIPHOOK_CONFIG`** to **outside** the repo (e.g. `/etc/...`) are not re-read after pull—use repo-local config if you want each push to pick up YAML changes automatically.
Multi-app mode is supported via `apps:` in `shiphook.yaml` (one process, one systemd service, multiple repos/domains). Each app defines its own `host`, `path`, `repoPath`, and `runScript`; if app `secret` is omitted, Shiphook auto-generates and persists a per-app secret file on first run. Requests for different apps run concurrently, while requests for the same app are serialized.
Full reference: **[Documentation](https://cap-jmk-real.github.io/shiphook/)**
Need step-by-step deployment examples? See **[Deployment recipes](https://cap-jmk-real.github.io/shiphook/deployment-recipes.html)** for single-app and multi-app on one server (DNS, GitHub Actions, secrets, server commands, and YAML).
---
## GitHub webhook (quick)
1. Repo → **Settings** → **Webhooks** → **Add webhook**.
2. **Payload URL:** your HTTPS URL (path must match `SHIPHOOK_PATH`).
3. **Content type:** `application/json`.
4. **Secret:** same as your Shiphook secret.
5. **Events:** e.g. **Just the push event**.
---
## Why Shiphook?
- **No vendor lock-in** — no deploy SaaS account; you control the box and the script.
- **Small surface** — one Node process, YAML or env, secret-based auth.
- **Fits real stacks** — `npm run deploy`, Docker, shell, whatever you already use.
---
## Programmatic use
```ts
import { createShiphookServer, ensureWebhookSecret, loadConfig } from "shiphook";
const config = loadConfig();
await ensureWebhookSecret(config);
const server = createShiphookServer(config);
await server.start();
```
---
## License
MIT.