Install

One command. Ten seconds. Done.

$ curl -fsSL https://buildandship.it/install.sh | sh

Works on macOS (Apple Silicon & Intel) and Linux (x86_64 & ARM). Installs a single ~8MB binary to /usr/local/bin. No runtime dependencies.

Authenticate

Sign in with GitHub. Takes 5 seconds.

$ bs login

Opens your browser, you click authorize, and you're in. Session lasts 30 days.

Deploy

Ship it.

$ bs deploy

Build & Ship detects your framework, builds a Docker image, starts it with zero-downtime blue-green deployment, and assigns you a public URL. The whole thing takes about 30 seconds.

> Detected Next.js 15 (confidence: 97%) > Building image... 6.1s > Blue-green swap... zero downtime > Health check 200 OK > Secure connection established Deploy Complete Project my-project Framework Next.js 15 URL http://localhost:4002 Public URL https://myapp-ember.buildandship.it Deploy ID a3f8c1d2 Duration 31.2s

Your URL

Share it with the world. It's live right now.

https://myapp-ember.buildandship.it

Every project gets a permanent .buildandship.it subdomain. It stays the same across every redeploy. Push new code, the URL updates instantly.

After a reboot, run bs restart to reconnect your secure connection and your URL is back up.

Custom Domain

Optional. Bring your own domain with automatic TLS.

$ bs domain add myapp.com

You'll be shown a DNS record to add. Once DNS propagates, SSL activates automatically via Cloudflare. Check progress anytime:

$ bs domain status myapp.com Domain: myapp.com SSL: active (auto-provisioned) DNS: CNAME → proxy.buildandship.it ✓

bs up

Your projects stay running. Forever. Like Kubernetes, but for humans.

When you run bs up, your project becomes self-sustaining. It survives reboots, crashes, Docker restarts, and code updates. One command replaces an entire Kubernetes stack.

$ bs up

What happens:

  1. Detects your framework and builds a Docker image (if no container exists)
  2. Starts the container with health checks enabled
  3. Registers the project for desired state — it should always be running
  4. Installs the Guardian daemon (auto-starts on boot)
  5. Sets up health monitoring, metrics collection, and local DNS
  6. Enables GitOps auto-deploy (watches your git repo for changes)
> Detected Next.js 15 (confidence: 97%) > Building image... bs-my-app:latest > Container running: bs-my-app (port 3000) > Health check: GET / every 15s > Auto-deploy: watching for git changes > Guardian daemon started (auto-starts on boot) Your app is always up. Forever. http://localhost:3000

After bs up, try killing the container — the Guardian brings it back within 15 seconds. Reboot your machine — everything comes back automatically.

bs up --status

The kubectl get pods of Build & Ship. See every managed project at a glance.

$ bs up --status GUARDIAN STATUS — 3 projects managed NAME STATUS UPTIME CPU RAM PORT HEALTH my-app Running 2d 14h 2.1% 128MB 3000 OK api-server Running 5h 22m 8.4% 256MB 8080 OK landing-page Stopped — — — 8888 Down Guardian: running (auto-starts on boot) Docker: healthy
No auth needed. bs up --status reads your local database — works offline, no login required.

Flags

--statusShow all managed projects (no deploy)
--no-auto-deploySkip GitOps watcher for this project

bs down

Gracefully stop a project. The Guardian stops watching it.

$ bs down my-app # stop one project $ bs down --all # stop everything

This stops the container, sets desired state to “stopped”, and the Guardian won’t restart it. Run bs up again to bring it back.

Data is preserved. bs down stops the container but doesn’t remove images or volumes. Your project is one bs up away from running again.

The Guardian Daemon

Your machine’s control plane. Runs in the background, keeps everything alive.

The Guardian is a background service that manages desired state for all your projects. It’s the equivalent of the Kubernetes kubelet — a daemon that runs on your machine and ensures reality matches intent.

How it works:

Every 15 seconds, the Guardian runs a reconciliation loop:

For each registered project: Container missing? → Re-create from saved image Container stopped? → Docker start Container unhealthy? → Restart with exponential backoff New git commits? → Rebuild and redeploy

Daemon lifecycle:

  • macOS: Runs as a Launch Agent (~/Library/LaunchAgents/com.buildandship.guardian.plist)
  • Linux: Runs as a systemd user service (~/.config/systemd/user/bs-guardian.service)
  • Auto-starts on boot (RunAtLoad / WantedBy=default.target)
  • Auto-restarts on crash (KeepAlive / Restart=always)
  • Logs: ~/.buildandship/guardian/guardian.log

Guardian commands (usually you don’t need these — bs up handles it):

$ bs guardian status # is it running? $ bs guardian logs # tail the daemon log

Self-Healing

Crashes happen. The Guardian handles them automatically.

The Guardian monitors two things per project:

  • Liveness: Is the container process alive? (checks Docker container state)
  • Readiness: Is the app actually serving traffic? (HTTP GET to health endpoint)

What happens when something breaks:

ScenarioGuardian Response
Container exits or crashesRestarts immediately
Container removed (docker rm)Re-creates from saved image
Health check fails 3 timesRestarts with exponential backoff
10+ restarts without recoveryMarks as CrashLoopBackOff, stops retrying
Docker daemon not ready (reboot)Waits up to 90 seconds, then reconciles

Exponential backoff: 10s → 20s → 40s → 80s → 160s → 300s (5 min max). If the project stays healthy for 5 minutes, the backoff counter resets.

GitOps

Push code, get a new deploy. Automatically.

For projects registered with bs up, the Guardian watches the local git repository for new commits every 60 seconds. When it detects changes:

  1. Checks if build-related files changed (Dockerfile, package.json, go.mod, etc.)
  2. Rebuilds the Docker image from the updated source
  3. Stops the old container
  4. Starts the new container
  5. Updates the database with the new container ID and commit SHA
Disable per-project: bs up --no-auto-deploy registers the project without GitOps. The Guardian still keeps it alive, just won’t auto-redeploy on code changes.

Metrics

CPU, memory, network, and response latency for every project.

The Guardian collects metrics every 15 seconds for each managed project:

MetricSourceShown in
CPU %docker statsbs up --status
Memory usagedocker statsbs up --status
Network I/Odocker statsStored in DB
Response latencyHealth check HTTP requestStored in DB
Container restartsGuardian reconcile loopbs up --status
UptimeContainer start timebs up --status

Metrics are stored in SQLite with 24-hour retention (auto-pruned to keep the database lean).


Commands — Auth

bs login

Opens your browser for GitHub OAuth. Creates a local session.

$ bs login

bs logout

Signs out and clears your session.

$ bs logout

bs whoami

Shows the currently logged-in user.

$ bs whoami

Commands — Deploy & Lifecycle

bs init

Detects your framework and generates buildandship.toml.

$ bs init $ bs init --deploy # init + deploy in one step

bs deploy

Build, deploy with zero downtime, set up a B&S secure connection, and register your public URL. Run in your project directory.

$ bs deploy # deploy current directory $ bs deploy ./my-app # deploy specific directory

bs list

See all your deployed projects at a glance — status, public URL, and whether auto-deploy is connected. Alias: bs ls.

$ bs list PROJECT STATUS PUBLIC URL AUTO-DEPLOY my-app ● live myapp-ember.buildandship.it ● user/my-app (main) api ● live api-coral.buildandship.it — old-site ○ stopped old-site-frost.buildandship.it —

bs status

Detailed project info with resource usage, deploy history, and domain mappings.

$ bs status # all projects (compact table) $ bs status myapp # detailed info for one project $ bs status myapp -n 10 # show last 10 deploys

bs logs

Stream container logs in real-time.

$ bs logs # current project $ bs logs myapp -n 100 # last 100 lines

bs stop

Stops containers and the secure connection. Data and images are preserved.

$ bs stop myapp

bs restart

Restarts containers and re-establishes the secure connection. Your public URL is automatically updated.

$ bs restart myapp

bs rollback

Reverts to a previous deploy using zero-downtime blue-green swap.

$ bs rollback # previous deploy $ bs rollback a3f8c1d2 # specific commit

bs up

Register a project for always-on management. Builds, deploys, and starts the Guardian daemon. See Always On for full details.

$ bs up # current directory $ bs up ./my-app # specific directory $ bs up --status # show all managed projects $ bs up --no-auto-deploy # skip GitOps watcher

bs down

Stop a project and remove it from Guardian management. Data and images are preserved.

$ bs down my-app # stop one project $ bs down --all # stop everything

bs destroy

Full teardown. Stops containers, closes the secure connection, removes your subdomain and custom domains, cleans up the database. Data volumes are preserved.

$ bs destroy myapp $ bs destroy --force # skip confirmation

Commands — Environment

Manage environment variables. Sensitive values (containing SECRET, TOKEN, PASSWORD, etc.) are automatically encrypted with AES-256-GCM.

bs env set

$ bs env set NODE_ENV=production $ bs env set DB_HOST=localhost DB_PASSWORD=s3cret

bs env list

$ bs env list

bs env rm

$ bs env rm DB_PASSWORD

Commands — Services

One-command managed databases. Connection strings are auto-injected into your .env file.

ServiceImagePortEnv Variable
postgrespostgres:16-alpine5432DATABASE_URL
mysqlmysql:83306MYSQL_URL
redisredis:7-alpine6379REDIS_URL
mongomongo:727017MONGODB_URI
$ bs service add postgres # start a database $ bs service ls # list running services $ bs service rm postgres # stop and remove

Commands — Domains

bs domain add

Map a custom domain (auto-TLS) or local domain (.local, .test, .dev via /etc/hosts).

$ bs domain add myapp.com # public domain $ bs domain add myapp.local # local domain

bs domain ls

List all domain mappings.

bs domain rm

$ bs domain rm myapp.com

bs domain status

Check SSL and DNS propagation status.

$ bs domain status myapp.com

Commands — CI/CD

Connects your GitHub repo for automatic deploys on every push. Run after bs deploy.

$ bs link # auto-detect from git remote $ bs link --repo user/app --branch develop # explicit repo + branch
--repoGitHub repo as owner/name (auto-detected from git remote)
--branchBranch to deploy on push (default: repo's default branch)

What it does:

  1. Starts a webhook agent daemon (auto-restarts on reboot)
  2. Stores deploy secrets in GitHub Actions
  3. Commits a workflow file to your repo
  4. Syncs your local repo (so you’re not behind after the remote commit)
  5. Sets your .buildandship.it URL in the repo’s About section on GitHub

How deploys work:

git push → GitHub Actions curl → Cloudflare Worker → B&S secure connection → localhost:19515 → git pull && bs deploy
Deploy first. Run bs deploy at least once before bs link. The link command needs your subdomain to exist.

Removes the CI/CD connection. Cleans up GitHub Actions secrets, workflow file, and subdomain route.

$ bs unlink

Commands — System

bs doctor

Health check. Verifies Docker, BuildKit, cloudflared, disk, memory, Git, and ports.

$ bs doctor

bs update

Self-update to the latest version.

$ bs update $ bs update --force # reinstall even if on latest

bs cleanup

Remove stopped containers, dangling images, and old build cache.

$ bs cleanup

bs version

Show current version, Go version, and OS/Arch.

$ bs version

bs uninstall

Completely remove Build & Ship from your machine. Stops all daemons, removes config/data, cleans shell profile, and deletes the binary. Requires typing “uninstall” to confirm.

$ bs uninstall # interactive confirmation $ bs uninstall --force # skip confirmation
Note: This does NOT remove deployed containers or remote resources (GitHub secrets, subdomain routes). Run bs destroy and bs unlink per-project first if needed.

Global Flags

-v, --verboseShow detailed output
-q, --quietSuppress all output except errors

Configuration

Project config lives in buildandship.toml at your project root. Auto-generated by bs init.

[project] name = "my-app" framework = "nextjs" [build] command = "npm run build" [deploy] port = 3000 health_check = "/" zero_downtime = true [domain] custom = "myapp.com"

Config priority (highest wins):

1. CLI flags 2. Environment variables (BS_PORT, BS_FRAMEWORK, etc.) 3. Project buildandship.toml 4. User ~/.buildandship/config.toml 5. Built-in defaults
VariableDescriptionDefault
BS_PORTOverride deploy port3000
BS_FRAMEWORKOverride detected frameworkauto
BS_MEMORYOverride memory allocation512MB
BS_WORKER_URLCustom Worker proxy URLhttps://proxy.buildandship.it

Supported Frameworks

Auto-detected by inspecting config files, dependencies, and project structure.

FrameworkLanguageDetectionPort
Next.jsJavaScriptnext.config.js/ts/mjs3000
RemixJavaScriptremix.config.js3000
NuxtJavaScriptnuxt.config.js/ts3000
SvelteKitJavaScriptsvelte.config.js3000
AstroJavaScriptastro.config.mjs4321
ViteJavaScriptvite.config.js/ts80
ExpressJavaScriptexpress dep3000
FastAPIPythonfastapi in imports8000
DjangoPythonmanage.py8000
FlaskPythonflask in imports8000
GoGogo.mod8080
RustRustCargo.toml8080
Static SiteHTMLindex.html80
DockerfileAnyDockerfile
Docker ComposeAnydocker-compose.yml

Package manager detection: package-lock.json → npm, yarn.lock → yarn, pnpm-lock.yaml → pnpm, bun.lockb → bun.

Node.js version: reads from .nvmrc, .node-version, or engines in package.json.

Blue-Green Deploys

Every deploy is a zero-downtime swap. No dropped requests.

1. Start new "green" container on free port 2. Run health checks against green 3. Health passes → drain "blue" container (30s grace) 4. Stop blue, green becomes the new blue 5. Set up B&S secure connection and register public URL If health check fails: → Stop green, keep blue running (automatic rollback)

Health Checks

After starting the green container, Build & Ship polls a health endpoint until it's consistently healthy.

SettingDefaultDescription
Endpoint/HTTP GET path to check
Interval2sTime between checks
Timeout120sMax time waiting for health
Min healthy10sMust stay healthy for this long
Success codes2xx, 3xxHTTP codes considered healthy

Customize in buildandship.toml:

[deploy] health_check = "/api/health" min_healthy_seconds = 15 drain_timeout = 60

DNS Guide

Point your domain to Build & Ship with a CNAME record:

Type: CNAME Name: @ (or www) Value: proxy.buildandship.it TTL: 600
ProviderSteps
GoDaddyDNS → Add Record → CNAME → proxy.buildandship.it
NamecheapAdvanced DNS → Add Record → CNAME → proxy.buildandship.it
CloudflareDNS → Add Record → CNAME → proxy.buildandship.it
Root domains: Some providers don't support CNAME on root domains (e.g., myapp.com vs www.myapp.com). Use www, transfer DNS to Cloudflare, or use a provider that supports ALIAS/ANAME records.