gofasta deploy
Deploys your application to a remote server via SSH. Supports two methods: Docker (default) — builds and transfers a Docker image, restarts a fixed Docker Compose project onto it; and binary — cross-compiles a Go binary, transfers via SCP, manages with systemd. Every deploy is gated on the app’s health endpoint; a release that fails its migrations or health check is automatically rolled back to the previous release.
Usage
gofasta deploy [flags]
gofasta deploy [command]Run this command from the root directory of your Gofasta project.
Flags
The flag set is persistent — every flag works on gofasta deploy and on every subcommand (including --dry-run).
| Flag | Type | Default | Description |
|---|---|---|---|
--host | string | Deploy target (user@server) | |
--method | string | docker | Deploy method: docker or binary |
--port | int | 22 | SSH port |
--path | string | /opt/<appname> | Remote deploy directory |
--arch | string | amd64 | Target architecture: amd64 or arm64 |
--dry-run | bool | false | Show commands without executing |
All flags can also be set in the deploy section of config.yaml or via GOFASTA_DEPLOY_* environment variables. Positional arguments are rejected — a typo like gofasta deploy statuss errors out instead of silently running a full deploy.
Subcommands
| Command | Description |
|---|---|
gofasta deploy setup | Prepare a fresh server: system packages, Docker (docker method) or service user + migrate CLI (binary method), directory structure, and — when deploy.domain is set — the nginx vhost |
gofasta deploy status | Show the current release and container/service state |
gofasta deploy logs | Tail application logs from the remote server |
gofasta deploy rollback | Restart the service on the newest release older than current, verify health, then move the pointer |
Examples
Deploy using config.yaml settings:
gofasta deployDeploy to a specific host:
gofasta deploy --host deploy@api.example.comDeploy as a compiled binary instead of Docker:
gofasta deploy --method binaryPreview any command without executing (works on all subcommands):
gofasta deploy --dry-run --host deploy@api.example.com
gofasta deploy setup --dry-run --host deploy@api.example.comFirst-time server setup:
gofasta deploy setup --host deploy@api.example.comCheck status, tail logs, or rollback:
gofasta deploy status
gofasta deploy logs
gofasta deploy rollbackConfiguration
Add a deploy section to your config.yaml (a scaffolded project ships one):
deploy:
host: deploy@api.example.com
method: docker # docker or binary
port: 22
path: /opt/myapp
arch: amd64 # amd64 or arm64
health_path: /health/ready
health_timeout: 30 # seconds
keep_releases: 3 # old releases retained for rollback
strict_host_key: accept-new # accept-new (default), yes, or no
domain: "" # nginx vhost domain, used by `deploy setup`health_path defaults to /health/ready — the readiness endpoint, which verifies real database and cache connectivity rather than bare process liveness. strict_host_key: yes requires the server’s host key to already be in known_hosts; the accept-new default pins on first connect.
Every deploy config value is validated at load time (they end up inside remote shell commands, so hostile values from a cloned repo’s config.yaml are rejected rather than executed).
Environment variable overrides use the GOFASTA_ prefix and cover every key, including multi-word ones:
export GOFASTA_DEPLOY_HOST=deploy@api.example.com
export GOFASTA_DEPLOY_METHOD=binary
export GOFASTA_DEPLOY_HEALTH_TIMEOUT=60How It Works
Docker Method
- Builds a Docker image locally using the project’s
Dockerfile, tagged with the release timestamp - Transfers the image to the server via
docker save | ssh docker load— no registry involved - Stages the release directory: the production compose file, links to
shared/.envandshared/config.yaml, and aRELEASE_IMAGEfile recording the image tag - Records which release is currently live (the rollback target)
- Cutover: points
currentat the new release and restarts the fixed compose project withAPP_IMAGEpinned to the new tag — a brief restart, after which containers, network, and the database volume are the same objects as before (data persists across deploys) - Runs database migrations inside the app container; a failure aborts and rolls back
- Polls the health endpoint; a failure rolls back to the previous release’s image
- Prunes old releases beyond
keep_releases(never the onecurrentpoints at) along with their images
Binary Method
- Cross-compiles a static binary (
CGO_ENABLED=0 GOOS=linux) fordeploy.arch - Transfers the binary, migrations, templates, and configs via SCP; installs the systemd unit
- Runs database migrations before cutover using the release’s own binary — a failure aborts without touching the running service
- Cutover: installs the binary to
/usr/local/bin/<appname>, pointscurrentat the release, restarts the systemd service - Polls the health endpoint; a failure reinstalls the previous release’s binary and restores the pointer
- Prunes old releases
Release Directory Structure
Deployments use a Capistrano-style release structure on the server:
/opt/myapp/
current -> releases/20260409-153000/ # pointer to the live release
releases/
20260409-153000/ # newest
20260409-120000/ # previous (available for rollback)
shared/
.env # production secrets, shared across releases
config.yamlEach deploy creates a new timestamped release directory. Cutover is restart-based (a brief blip, not zero-downtime); if the new release fails its health gate, gofasta automatically restores the previous release and deletes the failed release directory. On the very first deploy there is nothing to restore, so a failed release is left running for inspection.
Migration steps that were already applied are not reverted on rollback (golang-migrate has no automatic undo) — rollback restores the previous code.
Rollback
gofasta deploy rollback reverts manually:
- Lists releases by name and reads the
currentpointer (an unreadable pointer is a hard error — it never guesses) - Selects the newest release strictly older than current — a leftover directory newer than current is never chosen
- Activates it: Docker restarts the compose project on the release’s recorded
RELEASE_IMAGE; binary reinstalls the release’s binary and restarts - Health-checks the target, and only then leaves the
currentpointer on it — if the target is unhealthy, the release that was live before is restored
Errors
Deploy failures carry structured error codes (DEPLOY_HOST_REQUIRED, DEPLOY_CONFIG_INVALID, SSH_FAILED, HEALTH_CHECK_FAILED, DOCKER_COMMAND_FAILED, ROLLBACK_FAILED, MIGRATION_FAILED), each with a hint and docs link — in --json mode they arrive as machine-readable fields. See error codes.
Prerequisites
- SSH key-based access to the target server (password auth is not supported)
- Docker method: Docker installed locally and on the server
- Binary method: systemd on the server
- Your production
.envplaced at<path>/shared/.envon the server (deploys reuse it)
Use gofasta deploy setup to install prerequisites on a fresh Ubuntu/Debian server.
Troubleshooting
“deploy host is required” — Set deploy.host in config.yaml or pass --host.
“invalid deploy.path” / “invalid deploy.arch” / similar — Deploy config values are strictly validated; fix the offending value in config.yaml.
SSH connection refused — Verify the host, port, and that your SSH key is authorized on the server. Test with ssh -p <port> user@server echo ok.
Health check failed — The new release didn’t answer the health endpoint within health_timeout. Gofasta rolled back automatically and the previous release is serving again; run gofasta deploy logs to see why the new release failed. On a first deploy the failed release is left running for inspection.
Migrations failed — The deploy aborted (docker: rolled back; binary: the running service was never touched). Already-applied migration steps are not auto-reverted.
Docker not found on remote — Run gofasta deploy setup to install Docker on the server.