{"id":47622502,"url":"https://github.com/shipzero/zero","last_synced_at":"2026-04-01T22:23:20.980Z","repository":{"id":345618389,"uuid":"1186552711","full_name":"shipzero/zero","owner":"shipzero","description":"Deploy containers to your own server with one command. Automatic TLS, zero-downtime, no config.","archived":false,"fork":false,"pushed_at":"2026-03-20T11:48:57.000Z","size":186,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-20T13:41:25.248Z","etag":null,"topics":["deploy","deployment","docker","flyio","heroku","hosting","self-hosted","vercel","vps"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shipzero.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-19T18:43:24.000Z","updated_at":"2026-03-20T12:17:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/shipzero/zero","commit_stats":null,"previous_names":["shipzero/zero"],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/shipzero/zero","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shipzero%2Fzero","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shipzero%2Fzero/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shipzero%2Fzero/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shipzero%2Fzero/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shipzero","download_url":"https://codeload.github.com/shipzero/zero/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shipzero%2Fzero/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31292639,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T21:15:39.731Z","status":"ssl_error","status_checked_at":"2026-04-01T21:15:34.046Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deploy","deployment","docker","flyio","heroku","hosting","self-hosted","vercel","vps"],"created_at":"2026-04-01T22:23:20.277Z","updated_at":"2026-04-01T22:23:20.967Z","avatar_url":"https://github.com/shipzero.png","language":"TypeScript","readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"logo.png\" alt=\"zero\" width=\"320\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  The fastest way to ship containers to your own server.\u003cbr\u003e\n  One command. Automatic HTTPS. Zero config.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"zero.gif\" alt=\"zero deploy demo\" width=\"720\" /\u003e\n\u003c/p\u003e\n\n```bash\nzero deploy ghcr.io/shipzero/demo:latest\n# 🚀 Your app is live: https://demo.example.com\n```\n\nPlatforms like Vercel and Railway have great DX — but they're expensive, lock you in, and don't support arbitrary Docker images. Self-hosting is flexible and cheap, but getting a container live with HTTPS means wiring up nginx, Certbot, deploy scripts, and hoping nothing breaks.\n\nzero closes that gap. You point it at a Docker image, and it handles everything else — port detection, domain routing, TLS, health checks, zero-downtime swaps. Zero config files. Zero moving parts. Just one command and your app is live.\n\n## Quickstart\n\n### 1. Set up the server\n\n\u003e On your VPS\n\nAny Linux VPS with root access:\n\n```bash\ncurl -fsSL https://shipzero.sh/install.sh | sudo bash\n```\n\nThe installer sets up Docker, prompts for your domain and email (for TLS), and starts zero.\n\n### 2. Install the CLI\n\n\u003e On your machine\n\n```bash\ncurl -fsSL https://shipzero.sh/cli/install.sh | bash\n```\n\n### 3. Connect\n\n\u003e On your machine\n\n```bash\nzero login root@example.com\n```\n\nAuthentication uses SSH — if you can SSH into the server, you can use zero.\n\n### 4. Deploy\n\n\u003e On your machine\n\n```bash\nzero deploy ghcr.io/shipzero/demo:latest\n```\n\nThat's it. zero figures out the port, assigns a domain, provisions a TLS certificate, and routes traffic.\n\n```\n✓ Pulling image\n✓ Starting container\n✓ Detected port: 3000\n✓ Health check passed\n✓ Your app is live: https://demo.example.com\n```\n\n## Features\n\n- **HTTPS by default** — certificates are provisioned automatically\n- **Zero-downtime** — traffic switches only after the new version is healthy\n- **Preview deployments** — `zero deploy myapp --preview pr-21`\n- **One-command rollback** — `zero rollback myapp`\n- **Webhooks** — push to your registry, zero deploys it\n- **No reverse proxy config** — routing is built in\n- **Live metrics** — CPU, memory, and network in the terminal\n\nTwo dependencies. One container. No database. No web UI. No YAML.\n\n## Deploying\n\n### Docker images\n\nOnly the image is required. Everything else is inferred:\n\n| What       | How it works                                                      |\n| ---------- | ----------------------------------------------------------------- |\n| **Name**   | Last segment of the image path (`ghcr.io/shipzero/demo` → `demo`) |\n| **Port**   | Read from the image's `EXPOSE` directive, falls back to `3000`    |\n| **Domain** | `\u003cname\u003e.\u003cserver-domain\u003e` unless `--host-port` is set              |\n| **Health** | TCP connection check, or HTTP `GET` when `--health-path` is set   |\n\n```bash\n# Deploy with all defaults\nzero deploy ghcr.io/shipzero/demo:latest\n\n# Override any default\nzero deploy ghcr.io/shipzero/demo:latest --name api --domain api.example.com --port 8080\n\n# Redeploy an existing app\nzero deploy myapp\n\n# Deploy a specific tag\nzero deploy myapp --tag v1.2.3\n\n# Expose on a host port instead of a domain\nzero deploy ghcr.io/shipzero/demo:latest --host-port 8888\n```\n\nAll options:\n\n| Flag               | Description                                                       | Default                    |\n| ------------------ | ----------------------------------------------------------------- | -------------------------- |\n| `--name`           | App name (overrides inferred name)                                | _(from image)_             |\n| `--domain`         | Domain for routing and TLS                                        | _`\u003cname\u003e.\u003cserver-domain\u003e`_ |\n| `--port`           | Internal container port                                           | _(auto-detect)_            |\n| `--host-port`      | Expose directly on a host port (skips auto-domain)                | —                          |\n| `--tag`            | Image tag to deploy                                               | `latest`                   |\n| `--command`        | Container startup command                                         | —                          |\n| `--volume`         | Volumes, comma-separated (e.g. `pgdata:/var/lib/postgresql/data`) | —                          |\n| `--health-path`    | HTTP health check endpoint                                        | —                          |\n| `--health-timeout` | Health check timeout (e.g. `30s`, `3m`)                           | `60s`                      |\n| `--env`            | Env vars, comma-separated (e.g. `KEY=val,KEY2=val2`)              | —                          |\n| `--preview`        | Deploy as a preview environment                                   | —                          |\n| `--ttl`            | Time to live for previews (e.g. `24h`, `7d`)                      | `7d`                       |\n\n### Environment variables\n\nPass env vars inline with `--env`:\n\n```bash\nzero deploy ghcr.io/shipzero/demo:latest --env DATABASE_URL=postgres://localhost/mydb,SECRET_KEY=abc123\n```\n\nOr manage them separately — changes take effect on the next deploy:\n\n```bash\nzero env set myapp DATABASE_URL=postgres://localhost/mydb SECRET_KEY=abc123\nzero env list myapp\nzero env remove myapp SECRET_KEY\n```\n\n### Volumes\n\n```bash\nzero deploy postgres:16 --name postgres --port 5432 --volume pgdata:/var/lib/postgresql/data\n```\n\nFormat: `source:destination[:mode]`\n\n### Private registries\n\n```bash\nzero registry login ghcr.io --user \u003cusername\u003e --password \u003ctoken\u003e\nzero registry list\nzero registry logout ghcr.io\n```\n\n### Docker Compose\n\nFor multi-container apps:\n\n```bash\nzero deploy --compose docker-compose.yml --service web --name mystack --domain mystack.example.com --port 3000\n```\n\n| Flag             | Description                                                           |\n| ---------------- | --------------------------------------------------------------------- |\n| `--compose`      | Path to a `docker-compose.yml` file (required)                        |\n| `--service`      | The entry service that receives traffic (required)                    |\n| `--name`         | App name (required)                                                   |\n| `--image-prefix` | Shared image prefix for tag substitution (e.g. `ghcr.io/org/project`) |\n\nThe Compose file is uploaded to the server. On deploy, zero pulls images, starts services, and health-checks the\nentry service before routing traffic.\n\n**`--image-prefix` explained:** When you pass `--image-prefix ghcr.io/you/mystack`, zero replaces the tag of every\nimage in your Compose file that starts with that prefix. This is what makes `--tag`, webhooks, and preview\ndeployments work for Compose apps.\n\n```bash\nzero deploy --compose docker-compose.yml --service web --name mystack --image-prefix ghcr.io/you/mystack\n\n# Now these work:\nzero deploy mystack --tag v2               # updates all matching images to :v2\nzero deploy mystack --preview pr-21        # preview with tag :pr-21\n```\n\n## Operating apps\n\n### Logs and metrics\n\n```bash\nzero logs myapp              # stream app logs\nzero logs myapp --tail 500   # last 500 lines (default: 100)\nzero logs --server           # stream server logs\nzero metrics myapp           # live CPU, memory, network\n```\n\n```\nmyapp\n\n  cpu     ██████░░░░░░░░░░░░░░  28.3%\n  memory  ████████████░░░░░░░░  312 MB / 512 MB (60.9%)\n  net ↓   1.2 MB/s\n  net ↑   340 KB/s\n```\n\n### Rollback\n\n```bash\nzero rollback myapp\n```\n\nStarts a new container from the previous image and swaps traffic once healthy.\n\n### Start, stop, remove\n\n```bash\nzero stop myapp              # stop container, traffic returns 502\nzero start myapp             # restart and health-check before routing\nzero remove myapp            # remove app and all its containers\n```\n\n### Domains\n\nApps can have multiple domains. The first domain is the primary (used for preview subdomains).\n\n```bash\nzero domain add myapp staging.myapp.com     # add a domain (no redeploy needed)\nzero domain list myapp                      # list all domains\nzero domain remove myapp staging.myapp.com  # remove a domain\n```\n\nThe `--domain` flag on `zero deploy` sets the initial domain when creating an app. Use `zero domain add` for additional domains.\n\n### Deployment history\n\n```bash\nzero history myapp\nzero list                     # list all apps with status, URL, image\n```\n\n## Preview deployments\n\nSpin up a temporary version of any app:\n\n```bash\nzero deploy myapp --preview pr-21\n# =\u003e https://preview-pr-21.myapp.example.com\n```\n\nPreviews expire automatically (default: 7 days). One flag, temporary URL, automatic cleanup.\n\n```bash\nzero deploy myapp --preview feat-1 --tag feat-branch --ttl 24h\nzero logs myapp --preview pr-21\nzero metrics myapp --preview pr-21\nzero remove myapp --preview pr-21\n```\n\n## Webhooks\n\nEvery app gets a webhook URL and a secret. Push an image to your registry, zero deploys it automatically.\n\n```bash\nzero webhook url myapp\n# URL:    https://example.com/webhooks/myapp\n# Secret: a1b2c3d4e5f6...\n```\n\nConfigure both the URL and secret in GitHub Container Registry or Docker Hub. Payloads are verified with HMAC-SHA256.\n\nNon-matching tags automatically create preview deployments when the app has a domain.\n\n## How it works\n\n### Zero-downtime deployment\n\n1. **Pull** — image pulled from the registry\n2. **Start** — new container started on an ephemeral port bound to localhost\n3. **Health check** — TCP or HTTP check, up to 60 seconds\n4. **Swap** — reverse proxy route updated atomically, old container removed\n\nIf the health check fails, the new container is discarded. Traffic stays on the previous version.\n\n### Reverse proxy\n\nNo nginx. No Traefik. zero includes a built-in reverse proxy:\n\n- Routes requests to containers based on the `Host` header\n- TLS termination with automatic certificate selection (SNI)\n- Security headers: `Strict-Transport-Security`, `X-Content-Type-Options`, `X-Frame-Options`\n- Forwarding headers: `X-Forwarded-For`, `X-Real-IP`, `X-Forwarded-Proto`\n- Request timeout: 60s, max body size: 100 MB (configurable via `MAX_BODY_SIZE`)\n\n## Scope\n\nzero is a single-server deployment engine. One server, any number of apps.\nIf you need multi-node orchestration, team RBAC, or a web dashboard, zero is not the right tool.\n\n## Setup reference\n\n### Server requirements\n\n- Linux server (Ubuntu 22.04+ recommended)\n- Root access\n- A domain pointing to your server (for HTTPS and automatic subdomains)\n\n### Server configuration\n\nConfiguration is stored in `/opt/zero/.env`:\n\n| Variable                 | Description                                     | Default       |\n| ------------------------ | ----------------------------------------------- | ------------- |\n| `TOKEN`                  | Internal auth token (do not share)              | _(generated)_ |\n| `JWT_SECRET`             | Secret for signing JWT tokens                   | _(generated)_ |\n| `DOMAIN`                 | Server domain (used for app subdomains and TLS) | _(server IP)_ |\n| `EMAIL`                  | Let's Encrypt email (enables automatic TLS)     | —             |\n| `API_PORT`               | API server port                                 | `2020`        |\n| `CERT_RENEW_BEFORE_DAYS` | Renew certificates this many days before expiry | `30`          |\n| `PREVIEW_TTL`            | Default time to live for preview deployments    | `7d`          |\n| `MAX_BODY_SIZE`          | Maximum request body size for the reverse proxy | `100m`        |\n\n### Upgrade\n\n```bash\nzero upgrade --server        # upgrade remotely via CLI\nzero upgrade --canary        # install canary (pre-release) version\n```\n\nOr re-run the install script on the server.\n\n### TLS\n\nzero provisions and renews TLS certificates via Let's Encrypt automatically when `EMAIL` is set and `DOMAIN` is a\nreal domain (not an IP). Certificates are provisioned on first deploy and renewed within 30 days of expiry. HTTP\nrequests are redirected to HTTPS.\n\n### Uninstall\n\nServer:\n\n```bash\ndocker compose -f /opt/zero/docker-compose.yml down\nrm -rf /opt/zero /var/lib/zero\n```\n\nCLI:\n\n```bash\nrm -rf ~/.zero\n```\n\n## CLI Reference\n\n```\nzero \u003ccommand\u003e [options]\n\ndeploy \u003cimage-or-app\u003e [options]              Deploy an app (creates if new)\ndomain \u003cadd|remove|list\u003e \u003capp\u003e [domain]      Manage app domains\nenv \u003cset|list|remove\u003e \u003capp\u003e [args]           Manage environment variables\nhistory \u003capp\u003e                                Show deployment history\nlist                                         List all apps\nlogin \u003cuser@server\u003e                          Authenticate via SSH\nlogs \u003capp|--server\u003e [--tail \u003cn\u003e] [--preview \u003clabel\u003e]\n                                             Stream app or server logs\nmetrics \u003capp|--server\u003e [--preview \u003clabel\u003e]   Show live resource usage\nregistry \u003clogin|logout|list\u003e [server]        Manage registry credentials\nremove \u003capp\u003e [--preview \u003clabel\u003e] [--force]   Remove an app or preview\nrollback \u003capp\u003e [--force]                     Roll back to previous deployment\nstart \u003capp\u003e                                  Start a stopped app\nstatus                                       Show server connection info\nstop \u003capp\u003e [--force]                         Stop a running app\nupgrade [--server] [--all]                   Upgrade CLI and/or server\nversion                                      Show CLI and server version\nwebhook url \u003capp\u003e                            Show and rotate webhook URL\n```\n\n## Sponsors\n\n\u003ca href=\"https://codebeam.com\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"sponsors/codebeam_white.svg\" /\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"sponsors/codebeam_black.svg\" /\u003e\n    \u003cimg src=\"sponsors/codebeam_black.svg\" alt=\"codebeam\" height=\"30\" /\u003e\n  \u003c/picture\u003e\n\u003c/a\u003e\n\n## License\n\n[MIT](LICENSE)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshipzero%2Fzero","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshipzero%2Fzero","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshipzero%2Fzero/lists"}