Deployment
This guide walks through taking a build from your local machine to a production environment. It covers the prerequisites, the required environment variables, the deploy itself, and how to roll back if something goes wrong.
Prerequisites
- A built artifact (run
make buildor download a tagged release) - Access to the target environment and its secrets store
- A database migration plan, if the release includes schema changes
- A health check endpoint reachable at
/healthz
Environment variables
The service reads its configuration from the environment at startup. The most important variables are listed below. Anything not set falls back to the defaults in the configuration reference.
| Variable | Required | Description |
|---|---|---|
APP_ENV | Yes | production, staging, or development |
DATABASE_URL | Yes | Connection string for the primary database |
SECRET_KEY | Yes | Used to sign sessions and tokens. Must be at least 32 bytes. |
PORT | No | Port to listen on. Defaults to 8080. |
LOG_LEVEL | No | debug, info, warn, or error. Defaults to info. |
Deploy steps
1. Run database migrations
Run migrations before starting the new version. Migrations must be backward compatible with the version currently running so that a rolling deploy does not fail.
$ ./bin/app migrate up --env production
2. Start the new version
$ ./bin/app serve --env production --port 8080
Or, if you run under a container orchestrator, push the image and update the deployment:
$ docker build -t registry.example.com/app:1.8.0 .
$ docker push registry.example.com/app:1.8.0
$ kubectl set image deployment/app app=registry.example.com/app:1.8.0
3. Verify health
Wait for the health check to pass before routing traffic to the new instances.
$ curl -fsS https://app.example.com/healthz
{"status":"ok","version":"1.8.0"}
/healthz to return 200 before marking an instance as healthy. You do not need to add extra delay.
Rolling back
If the new version misbehaves, redeploy the previous image. Database migrations are not reversed automatically.
$ kubectl rollout undo deployment/app
migrate down in production unless you have confirmed that no data will be lost. Rolling back a destructive migration can delete rows or columns permanently.
Troubleshooting
The service exits immediately on startup
Check that SECRET_KEY is set and at least 32 bytes long. The service refuses to start in production mode without it.
Health checks fail after deploy
Confirm that DATABASE_URL is reachable from the new instance. Network policies and security groups are the most common cause.
Requests return 502 from the load balancer
The application is probably listening on a different port than the one the load balancer targets. Make sure PORT matches the target group configuration.