Docs / Candor Deploy
The .candorfile: telling us how to run your app
The optional file you commit next to your code to set the build directory, port, builder, start command, databases, and environment — and how it takes precedence over everything else.
Candor Deploy works with no configuration — we read your repository and figure out how to build and run it. The .candorfile is for the times you’d rather say it yourself than have us guess: which subdirectory to build, which port your process listens on, what databases it needs, how it starts.
It’s optional. Most apps never need one. When you do use it, it’s the most explicit thing you can say about your app — it lives in your repository, it’s read at the exact commit being built, and it travels with your code.
Where it goes and what it looks like
Commit a file named .candorfile (or .candorhost) at the root of your repository. It’s YAML, one block per service:
services:
api:
root: backend
port: 4000
start: node build/index.js
database: postgres
env:
LOG_LEVEL: info
JWT_SECRET: {generate: hex32}
The service name (api here) matches the service in your portal. A repository with a single app can name its one block anything.
Where it sits in the order of things
Three things can have an opinion about a setting. The more explicit one wins, always in this order:
- The
.candorfile— this file. Highest priority. - Your portal settings — the fields you filled in the “Deploy an app” form.
- Detection and defaults — what we read from your repo, plus platform defaults (Node 22, port 8080, Nixpacks).
So a value here overrides both the portal form and anything we detected — including your repository’s own package.json start script. Nothing we detect ever overrides this file.
The keys
Everything below is optional; set only what you want to pin.
| Key | What it does |
|---|---|
root | Subdirectory to build from, for a monorepo. A relative path inside the repo (no ..); blank builds the repository root. |
port | The port your process listens on. You can also just read the PORT environment variable, which we always set — see Builds and configuration. |
start | The command that starts your app. Overrides your package.json start script and anything we’d supply. |
builder | nixpacks (our detection, the default) or dockerfile (build your repo’s Dockerfile). Naming a Dockerfile opts you in on its own — see below. |
dockerfile | Path to a Dockerfile to build, relative to root. Setting this implies builder: dockerfile. |
kind | app (has a URL), worker (background process, no URL), or static (built and served as files). |
memory | Memory to request, in MB. (memory_limit_mb is accepted as a synonym.) |
database / databases | A database engine — postgres, mysql, or redis — or a list of them. We provision each and wire its connection string into your environment. |
env | Environment variables (see below). |
Anything we don’t recognize is reported back to you in the portal as a readable problem, not silently ignored — so a typo in a key name never passes quietly.
Building from your own Dockerfile
By default we ignore a Dockerfile in your repo and build with our own detection. To use yours, opt in:
services:
web:
builder: dockerfile
That builds the Dockerfile in the directory being built. To name a different one, use dockerfile: (relative to root) — that opts you in on its own, so you don’t need both lines:
services:
web:
dockerfile: docker/Dockerfile.prod
The full reasoning for why Dockerfiles are opt-in is in Builds and configuration.
Environment and generated secrets
env sets environment variables. Give a literal value, or ask us to generate a secret you never have to see or store:
env:
NODE_ENV: production
SESSION_SECRET: {generate: hex32}
{generate: hex32} mints 32 random bytes on our side and injects them as the value — the secret is created for you and never lives in your repository. Variable names follow the usual shell convention (uppercase letters, digits, and underscores).
When it’s wrong, you’ll know
The file is parsed strictly and safely: anyone can commit one, so we never trust it blindly. Every problem — an unknown key, a bad port, an invalid builder — comes back as a plain message in the portal, and the rest of your valid settings still apply. The file is capped at 64 KB; anything larger than that isn’t an honest config and won’t be read.
Monorepos: many apps in one repository
Point Candor Deploy at a repository with more than one app and it detects each one and offers to deploy them together as a single project, wired to each other.
- The public app is chosen for you, and you can change it. When we detect several apps, we guess which one is public-facing — a frontend framework, or the app not named like a backend — and mark it with a “Public” badge on the review screen when you deploy. That’s the app your custom domain attaches to. If we guessed wrong, move the badge to any app on that screen before you deploy; this is a portal choice, not something you set in
.candorfile. - App-to-app URLs are injected automatically. Every app in the project receives each sibling app’s in-cluster address as an environment variable named
<DIR>_URL— abackendservice becomesBACKEND_URL=http://svc-..., reachable from the other apps over the private network. Set the same variable yourself (here or in the portal) and your value wins; auto-wiring never overwrites a value you chose.
As everywhere else, .candorfile is the final word on how each app builds and runs: .candorfile > portal settings > what we detect.
Something here wrong, unclear, or missing? Tell us — docs get fixed like bugs.