Deploy an app
Build a container from a Dockerfile, run it in its own microVM, and reach it over HTTPS
Goal. A repository with a Dockerfile becomes a running app with a public HTTPS
hostname. The example is acme's api: a Go HTTP server in api/, listening on 8080.
For an image that already exists (no build), see Variations.
Declare the app
version: 1
name: acme
project: acme-ad19
environments:
prod:
apps:
- name: api
build_context: ./api # ./api/Dockerfile
targets:
- name: primary
plan: app-sbuild_context is the directory docker build runs in, relative to pier.yaml; the
Dockerfile is read from there (dockerfile: on the target overrides the path). plan is
the target's size (Plans). project: is the slug the platform generated
when the project was created; pier config init --name acme writes it.
Deploy
pier deploy→ Comparing prod environment against platform state...
Changes:
+ create environment "prod" (new environment)
+ create app "api" in prod (plan: app-s, replicas: 1)
+ create postgres "main" in prod (plan: pg-s, version: 17, replicas: 1)
+ create bucket "uploads" in prod (access: private)
+ create site "www" in prod
Deploy:
app "api" build (first deploy)
site "www" build required (new resource — initial build required)
postgres "main" deploy (deployed inline)
bucket "uploads" deploy (deployed inline)
OK Applied 5 config change(s) to prod
→ Building api from ./api
→ Building pier-build/api:latest...
→ Pushing registry.pier.run/01a05313-e4fa-7707-a014-05f1ae901cec/api:9eed0df...
9eed0df: digest: sha256:13e134f00256cce6b3bc076b2671fb63dca2eee0bfb14509576612703a39c662 size: 855
OK Pushed registry.pier.run/01a05313-e4fa-7707-a014-05f1ae901cec/api:9eed0df
→ Deploying api...
→ Found 1 site files to deploy
→ Uploading 1 files...
OK All files uploaded
→ Waiting for infrastructure provisioning...
OK Infrastructure ready
OK Summary: 1 built, 4 deployed
deploy first converges the whole environment's declaration; the create … lines are
the pier apply step that deploy implies, so acme's Postgres, bucket and site are
provisioned in the same run. It then builds the image with the local docker, pushes it to
the project's registry with the credentials pier login installed, and rolls the target.
A new app reaches running when its first instance passes its readiness check.
Reach it
pier info apiName api
ID app_01m19h7sdyef8v937vjvbjw7hj
Environment prod
Target primary
Deployment depl_01m19h7sefeemtmtcc334e1wax
Status running
Replicas 1
Image registry.pier.run/01a05313-e4fa-7707-a014-05f1ae901cec/api:9eed0df
Port 8080
URL https://9z2vay2eae6.app.pier.run
CPU 500m
Memory 512Mi
Created 2026-08-30 22:30:08
Updated 2026-08-30 22:30:08
INSTANCE PHASE READY RESTARTS STARTED
j675p Running yes 0 42s ago
curl -sS https://s9vmcq9byrx.app.pier.run/
curl -sS https://s9vmcq9byrx.app.pier.run/jsonhello from app-01a05313-e5c2-70df-acd5-6de1595d13b8-666765455b-j675p
{"host":"app-01a05313-e5c2-70df-acd5-6de1595d13b8-666765455b-j675p","status":"ok","version":"1.0.0"}
The hostname is keyed by a random id, not by the app's name, so renaming api later
does not change it. A custom domain attaches your own name.
Watch it
pier logs api --tail 5 api · the last hour
2026-08-30 22:31:46.362 INFO [t5dk7] 2026/08/30 14:31:46 [tick 8] hello-web running on app-01a05313-e5c2-70df-acd5-6de1595d13b8-58946dd54b-t5dk7
2026-08-30 22:31:51.366 INFO [t5dk7] 2026/08/30 14:31:51 [tick 9] hello-web running on app-01a05313-e5c2-70df-acd5-6de1595d13b8-58946dd54b-t5dk7
2026-08-30 22:31:56.366 INFO [t5dk7] 2026/08/30 14:31:56 [tick 10] hello-web running on app-01a05313-e5c2-70df-acd5-6de1595d13b8-58946dd54b-t5dk7
2026-08-30 22:32:01.370 INFO [t5dk7] 2026/08/30 14:32:01 [tick 11] hello-web running on app-01a05313-e5c2-70df-acd5-6de1595d13b8-58946dd54b-t5dk7
2026-08-30 22:32:06.374 INFO [t5dk7] 2026/08/30 14:32:06 [tick 12] hello-web running on app-01a05313-e5c2-70df-acd5-6de1595d13b8-58946dd54b-t5dk7
pier logs api --follow streams; pier monitor api shows CPU, memory and requests;
pier events lists what the platform did.
What changed
The declaration above is the whole change. pier deploy recorded a deploy for api
(pier deploys ls api) with the digest of the image it built; a
rollback returns to that record.
Variations
-
A pre-built image, no build: set
image:andport:on the target instead of abuild_context.pier deploypulls the image and does not run docker.pier.yaml apps: - name: whoami targets: - name: primary image: traefik/whoami:v1.10.1 port: 80 plan: app-s -
No Dockerfile: Pier detects Node, Python, Go, Rust, Ruby, PHP, Java, .NET, Elixir and Deno projects and builds them without one. Add a
Dockerfileto control the build. -
From the CLI instead of the file:
pier add app --name api --plan app-sdeclares the same thing and writes it topier.yaml;--stagewrites without applying. -
Build on the platform on each push: Deploy from GitHub.