# Ship Ship turns a VPS into a self-hosted git server with a web frontend — similar to running cgit on your own domain. Visiting your base domain in a browser shows a cgit repo index; clicking through shows trees, commit logs, diffs, and blame. Public repos are cloneable over HTTPS. If you host Go code, `go get` works with your domain out of the box. This is the read-only, public-facing side, and it works exactly the way cgit users expect. The difference is what happens on the write side. When you `git push` over SSH, Ship doesn't just update the bare repo — it builds and deploys your code. A post-receive hook checks out the repo, runs `docker build`, installs a systemd service and Caddy reverse-proxy config, and restarts the app. Your deployment config (the systemd unit, the Caddyfile) lives in `.ship/` in your repo and is versioned alongside your code. Push to main and it's live; push to any other branch and nothing happens. Not every repo needs to be a running service. If there's no Dockerfile, the push is accepted but the deploy step is skipped — the repo just sits there as a browsable, cloneable library. This makes Ship useful for Go modules that only need vanity imports and a public source view, alongside apps that need the full build-and-deploy pipeline. The same base domain serves both. Ship also supports direct deploys (SCP a binary or rsync a static directory) for cases where git push isn't the right fit. Ship is a client-side CLI. All state lives on your laptop at `~/.config/ship/state.json`. The VPS is configured entirely over SSH — no agent or daemon runs on the server. ## Install ``` go install github.com/bdw/ship/cmd/ship@latest ``` Or build from source: ``` go build -o ship ./cmd/ship ``` ## Quick start ### 1. Set up the VPS ``` ship host init --host user@your-vps --base-domain example.com ``` This installs Caddy, Docker, git, fcgiwrap, and cgit. It creates a `git` user for push access, configures sudoers for deploy hooks, and enables automatic HTTPS. The host becomes the default for subsequent commands. ### 2. Deploy **Git push (Docker-based app):** ``` ship init myapp git add .ship/ Dockerfile git commit -m "initial deploy" git push origin main ``` **Git push (static site):** ``` ship init mysite --static git add .ship/ index.html git commit -m "initial deploy" git push origin main ``` **Git push (library / Go module):** ``` ship init mylib --public git add . git commit -m "initial" git push origin main ``` No Dockerfile, so nothing is deployed — the repo is just browsable and cloneable at `https://example.com/mylib`. **Direct (pre-built binary):** ``` GOOS=linux GOARCH=amd64 go build -o myapp ship --binary ./myapp --domain api.example.com ``` On first deployment, Ship creates a `.ship/` directory in your current working directory containing: - `.ship/service` - systemd unit file - `.ship/Caddyfile` - Caddy reverse proxy config These files are uploaded on each deployment. You can edit them locally to customize your deployment (add extra Caddy routes, adjust systemd settings). The systemd service is regenerated when you update resource limits with `--memory`, `--cpu`, or `--args` flags. The Caddyfile is never regenerated, so your custom routes won't be overwritten. You can version control `.ship/` or add it to `.gitignore` — it's your choice. ## Commands ### `ship init ` Create a bare git repo on the VPS and generate local `.ship/` config files. ``` ship init myapp # Docker-based app ship init mysite --static # static site ship init myapp --domain custom.example.com # custom domain ship init mylib --public # publicly cloneable (for go get) ``` ### `ship deploy ` Manually rebuild and deploy a git-deployed app. ### `ship [deploy flags]` Deploy a pre-built binary or static directory directly. ``` ship --binary ./myapp --domain api.example.com ship --binary ./myapp --domain api.example.com --env DB_HOST=localhost ship --static --dir ./dist --domain example.com ship --name myapi --memory 512M --cpu 50% ``` Flags: `--binary`, `--static`, `--dir`, `--domain`, `--name`, `--env`, `--env-file`, `--args`, `--file`, `--memory`, `--cpu` ### `ship list` List all deployments on the default host. ### `ship status/logs/restart/remove ` Manage a deployment's systemd service. ### `ship env` ``` ship env list myapp ship env set myapp KEY=VALUE ship env unset myapp KEY ``` ### `ship host` ``` ship host init --host user@vps --base-domain example.com ship host status ship host update ship host ssh ``` ### `ship ui` Launch a local web UI for viewing deployments. ## VPS file layout ``` /srv/git/.git/ # bare git repos /srv/git/.git/hooks/post-receive # auto-deploy hook /var/lib//src/ # checked-out source (for docker build) /var/lib//data/ # persistent data volume /var/www// # static site files /etc/systemd/system/.service # systemd unit /etc/caddy/sites-enabled/.caddy # per-app Caddy config /etc/caddy/sites-enabled/ship-code.caddy # base domain Caddy config /etc/cgitrc # cgit configuration /etc/ship/env/.env # environment variables /etc/sudoers.d/ship-git # sudo rules for git user /opt/ship/vanity/index.html # vanity import template /home/git/.ssh/authorized_keys # SSH keys for git push ``` ## Supported platforms VPS: Ubuntu 20.04+ or Debian 11+ ## Security See [SECURITY.md](SECURITY.md) for the threat model, mitigations, and known gaps. ## License MIT