Deploying apps
Paas deploys any application that builds into a Docker image from a
Dockerfile at the repo root, listens on a single TCP port, and serves HTTP.
The contract your app must satisfy
Dockerfileat the repo root. Buildpacks are not in v1.- Listen on the port the platform tells you. Default is
8080, set when you create the app and changeable later. The platform setsPORTin the environment, so prefer that over hardcoding. - Bind to
0.0.0.0, not127.0.0.1. The container's IP is reached from outside the container by nginx. - Respond
< 500toGET /within 30 seconds of starting. That's the default health probe. (To use a different path, configureOrchestrator__HealthPathin the orchestrator service.)
Triggering a deploy
Three ways:
| How | When |
|---|---|
git push paas main |
Normal flow. New commit → new deployment. |
| Dashboard → app → Redeploy current | Same commit, new container (e.g. to pick up env-var changes). |
POST /api/orgs/{org}/apps/{app}/deployments/redeploy |
API equivalent. |
Only main (well, any branch, but the default-branch convention) triggers a
deploy. Tag pushes are ignored.
Setting environment variables
Dashboard → app → Environment variables, or:
TOKEN=<your jwt>
curl -X PUT https://paas.example.com/api/orgs/acme/apps/web/env/DATABASE_DEBUG \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"value":"true"}'
Env-var changes do not auto-deploy — they apply on the next deploy or redeploy. This is intentional: env changes shouldn't silently alter behavior for users who didn't ship a release.
Reserved env vars Paas always injects:
| Var | Value |
|---|---|
PORT |
The port your app must listen on (default 8080). |
PAAS_APP |
App slug. |
PAAS_RELEASE |
Release UUID, useful for log tagging. |
DATABASE_URL |
Set automatically if a managed DB named main exists. |
Build context
The Builder runs docker build against the entire repo at the pushed
commit. Use a .dockerignore to keep your image lean — node_modules, .git,
build outputs, etc.
The build container has a Docker socket (so it can talk to the engine) and
runs in its own container. You don't get the host's Docker config — if your
build needs private base images, set credentials via build-time secrets in
your Dockerfile (BuildKit supports this) and store them as env vars on the
app, then pass via --secret.
Build logs
Each deployment's build log is captured and shown in the dashboard:
acme → web → click a Deployment ID → see the streamed docker build
output. Logs are persisted in the build_log_lines table so you can scroll
through old builds.
Common gotchas
- 502 right after push. The new container is starting; the proxy hasn't swapped yet. Wait ~10s.
- Container starts then immediately restarts. Likely your app is
listening on
127.0.0.1. Bind to0.0.0.0:$PORT. - Build is slow. First builds pull base images. Subsequent builds reuse
layers as long as your
Dockerfileorder is layer-cache-friendly (deps before source, etc.). DATABASE_URLnot appearing. The managed DB has to be named exactlymainand have statusReadybefore the next deploy.
Rolling back
A "rollback" is just redeploying an older deployment. From the API:
curl -X POST https://paas.example.com/api/orgs/acme/apps/web/deployments/redeploy \
-H "Authorization: Bearer $TOKEN"
(Rollback to an arbitrary previous SHA is a v1.1 feature — for now, redeploy
re-uses the most recent live one. As a workaround, push the old commit on
top of main.)