Environment variables and secrets
Set configuration per application, share it across a project with variable sets, and keep secrets out of Git.
Configuration that changes between environments — a database URL, an API token, a feature flag — belongs in environment variables, not in the repository. DockBoard stores them encrypted and injects them at container start.
Per application
The Environment tab on an application. Add keys and values, save, redeploy. Values are encrypted at rest and are never returned in a deployment log or a build output.
Shared across a project
Six applications that all need the same REDIS_URL should not carry six copies of it. A project variable set is defined once and reaches every application in the project.
A set is either project-wide or scoped to one environment. That gives you the shape you actually want: one DATABASE_URL for staging, another for production, under the same key name.
Which value wins
Three layers, narrowest last. Later beats earlier:
project-wide set REDIS_URL=redis://shared:6379
↓ beaten by
environment set REDIS_URL=redis://staging:6379
↓ beaten by
the application itself REDIS_URL=redis://localhost:6379The dashboard shows, for every key an application will actually receive, which layer it came from. That is the answer to “why is this variable not what I set” without having to reason about the order yourself.
Variables your repository already carries
A deployment from Git does not ignore the .env files committed in the repository — a project that boots locally on its committed defaults should boot here too. They are read from the clone and merged beneath everything set in DockBoard, so nothing in the repository can quietly displace a value you set in the dashboard.
Within one folder, five names are read in this order, each beating the ones before it:
.env.example → .env.local.example → .env.production → .env → .env.local.env.example is read on purpose, at the lowest priority: a repository that ships only a template still deploys on the first try. But when nothing else defines a key, that placeholder becomes the production value — so the build log names every key supplied only by a template, and flags the ones that still look unedited. JWT_SECRET=change_me in a public repository is a public secret.Monorepos: two folders, not one
When an application lives in a sub-folder of a monorepo, the five names above are read twice: once at the clone root, once in the application’s own sub-folder. The sub-folder wins.
That is what makes a monorepo root .env behave the way its authors intended — shared defaults for every package, each package free to override what concerns it. Nothing is configured to obtain this; it applies as soon as the application has a sub-folder path.
The complete order
Five layers, for a monorepo application deployed from Git in a project that shares variables. Later beats earlier, without exception:
1 monorepo root .env* committed, shared by every package
2 sub-folder .env* committed, belongs to this application
3 project-wide set DockBoard, every environment
4 environment set DockBoard, one environment
5 the application itself DockBoard, this applicationThe dividing line is between 2 and 3: everything committed to the repository sits below everything set in DockBoard. A DATABASE_URL in a committed .env is a local-development default; the one in your project set is the real one, and it wins on every deployment.
.env; the build log names them.How secrets are handled
- Values are encrypted at rest with the instance encryption key, not stored as plain columns.
- Reading or writing them needs
apps:env— a permission that is not part of the read-only role. Someone who can see your application does not automatically see its secrets. See roles. - Editing the shared project set is gated on the same permission as the per-application tab. It holds production secrets exactly as the app tab does, so it must not be the cheaper door.
- An API key reaches them only with the
project:apps:envscope, which you can decline to grant while still granting deploy rights.
apps:exec). If a value must not be readable by the people who operate the application, it does not belong in an environment variable at all.Variables that DockBoard fills in for you
A managed database exposes a connection string you can paste straight into a variable, already pointing at the project network name rather than at a public address. That is the intended path: applications reach their database over the private network, and the database is never published.