Services
Long-running processes: image or build, command, ports, health, public hosts, resources and the develop loop.
| Field | Meaning | Default |
|---|---|---|
image | A prebuilt image. | One of image or build is required |
build | . (a directory with a Dockerfile), { context, dockerfile, args, paths }, or { from: <service> } to reuse another service's build. | |
command | Replaces the image's command. The entrypoint stays, as in Compose. | The image's command |
ports | { <name>: <number>, ... }. The first one is the service's port. | { http: 8080 } when the service needs a port, else none |
health | { http: /path } on the first port, or { tcp: <port> }. | Running means healthy |
expose | true (public on the first port, with the service's name as host), or a list of { port, host, path }. | Not public |
env_from | Env groups the service gets. | |
externals | Externals the running service and its jobs call. Every service of the sandbox may reach them. | |
depends_on | Managed entries (gives their standard variables) and services (start after they're healthy). | |
env | Variables. See env. | |
user | "<uid>[:<gid>]", numbers only and not 0. | The image's user |
resources | { requests: { cpu: 250m, memory: 512Mi }, limits: { memory: 1Gi } } | 100m CPU and 256Mi requested, 2 CPU and 2Gi limit |
kubernetes | { patch: ... }, see the escape hatch. | |
develop | Compose's develop block: files copied into the running service instead of a rebuild, and a dev command. See Develop. Sandboxes only. | Every edit rebuilds the image |
- Commands. A string of plain words (
npm run worker) runs directly. A string that uses the shell (&&,|,$VAR, quotes,cd) runs assh -c "<string>". A list ([node, server.js]) runs exactly as written, for images without a shell. PORTis set to the service's first port, as on Cloud Run, Render and Heroku. An app that readsPORTneeds noports. An image that listens on a fixed port (nginx on 80) declaresports: { http: 80 }instead. SettingPORTto another number than the first port is refused, since traffic and health checks go to the port.- Non-root only. Containers can't run as root. If your image's user is root or a name rather than a number, use a numeric
USERin the Dockerfile (USER 10001rather thanUSER app, the uid youradduser -umade), or setuser: "10001"on the service. Volumes are writable by that user. To find a named user's uid:docker run --rm --entrypoint id <image>. If you forget, the sandbox fails as soon as that container is created (after the setup jobs, and after the services it depends on are up), and the message says which of the two it is. - Public URLs are
<host>--<sandbox>--<account>.<domain>, one DNS label, so the three together must fit in 63 characters. A host of up to 21 characters always fits.parity checkprints them with<sandbox>and<account>in place of the IDs, refuses a host over 55 characters and warns over 21. - Paths aren't stripped.
expose: [{ host: app, path: /api }]sends/api/...to the service with the path unchanged, so the app serves its routes under/api. Two services can share a host this way, which avoids CORS. - Ordering.
depends_on: [api]holds this service untilapipasses its health check, like Compose'scondition: service_healthy. It applies at start only. Cycles are refused. - Replicas. Sandboxes run one replica of each service.
parity scale <service> <n>changes it for one sandbox, and 0 stops a service. - OpenTelemetry.
OTEL_SERVICE_NAMEandOTEL_RESOURCE_ATTRIBUTESare set for you, so any OTel SDK tags traces, metrics and logs with the service and sandbox. - No persistent volumes. A service's filesystem resets when it restarts. Keep data in a managed entry: Postgres, Redis or S3.
Builds
parity up uploads your working directory (without .git and what .dockerignore leaves out, though each Dockerfile goes, as with docker build) and builds each build block, once per distinct block. Services built { from: web } share web's image. A build Parity has already done for the same files and args is reused across all your sandboxes, so a second sandbox on the same code starts without building. --image web=<ref> skips the build for that service and the services built from it. A failed build fails the sandbox and shows the build log.
contextis a directory inside the oneparity upuploads, anddockerfilea path insidecontext. Absolute paths and..out of either are refused.- Network. A build reaches package registries (npm, PyPI, Go, crates.io, RubyGems, Maven Central, Packagist, Debian, Ubuntu and Alpine mirrors) and base images on Docker Hub, ghcr.io, quay.io, gcr.io and mcr.microsoft.com. Nothing else. To download from another host (Prisma engines, Playwright browsers, a GitHub release), add it to
externalsand name it inbuild.externals: [prisma]. A build that tries another host fails, and the error names the host. - Trigger paths.
build.pathslists the files whose changes need a new image, as globs relative to parity.yaml with.dockerignore's syntax (**crosses directories). The default is everything under the context. Set it when a service also depends on files outside its context:build: { context: apps/web, paths: [apps/web/**, packages/ui/**] }. The default also covers the Dockerfile and the.dockerignorenext to parity.yaml. On each push to your default branch, Parity works out which services changed since the last built push; the others keep their image. - Builds from a push use GitHub's archive of the commit, which differs from your working tree in three ways: files marked
export-ignorein.gitattributesare left out, Git LFS files arrive as their pointer files, and submodules are empty. Build those in the Dockerfile instead (fetch LFS objects or clone the submodule there). - Builds see no secrets and no sandbox values, only
build.args.parity checkwarns when an arg differs per sandbox (a public URL, a generated value), because that means one build per sandbox. Prefer reading such values at run time.
Develop
By default every edit rebuilds the service's image, which is what staging and production run. A develop block, Compose's develop spec, lets an edit skip the build in sandboxes. Parity knows no language or framework: your repo says which directories are copied and whether the process restarts.
services:
api:
build: { context: ./api }
develop:
watch:
- { path: ./api/src, target: /app/src, action: sync+restart }
- { path: ./api/src/requirements.txt, action: rebuild }
web:
build: { context: ./web }
develop:
command: npm run dev
watch:
- { path: ./web/src, target: /app/src, action: sync }action | What parity up does when a file under path changes |
|---|---|
sync | Copies the changed files into the running container. Nothing restarts, so your app's own reloader, if it has one, picks them up. |
sync+restart | Copies them and restarts the service on the same image, in a new pod, so the process starts on the new files. |
rebuild | Builds a new image. Every file outside a sync rule already does, so this matters only for a file inside a synced directory, such as a lock file. |
pathis a directory (arebuildrule may also name a file), relative to the file that declares it, inside the upload.targetis where it appears in the container, an absolute path. The target is replaced, not merged: what the image has there is hidden. So point it at a directory of source only (/app/src), never at one that also holds what the build installed (/app, with itsnode_modules).- No build for synced edits. The build ignores the files under sync paths, so an edit there reuses the image, and so do your other sandboxes whose code differs only there. A new sandbox still builds once if no sandbox has built that image yet.
- The first copy comes before the start. A new pod, and each deploy job of a rollout, gets the whole directory before its process starts, so it never runs the image's older copy. Cron jobs and
parity runuse the image as built. - Dev mode is opt-in.
parity up --dev webrunsdevelop.command(yournext dev,viteorair) instead of the image's command. Without adevelop.command,--devis an error rather than a guess. Eachparity upstates the services it runs in dev mode. - Check the real build.
parity up --buildignores the sync rules and builds every image as staging and production run it. Run it before recording proof. - Not supported:
ignore(leave files out of the upload with.dockerignore), and therestartandsync+execactions.initial_syncis accepted and has no effect, since a new container always gets the whole directory. - Staging and production ignore
develop.