Configuration reaches your app as environment variables. Each service has its own set, edited in its Variables tab as KEY=value lines.
Variables are stored encrypted, and values stay masked on screen until you choose to reveal them.
Hangar also sets PORT on every service, to the container port of its first domain, unless you set it. See Domains and HTTPS.
Three scopes
| Scope | Belongs to | Use it for |
|---|---|---|
| Service | One service | Settings only that service needs |
| Environment | One environment of a project, such as production | Values every service in that environment shares, such as a database URL |
| Project | The whole project | Values that are the same in every environment |
References
Project and environment variables are not injected into services on their own. A service takes one by referencing it:
DATABASE_URL=${{environment.DATABASE_URL}}
SENTRY_DSN=${{project.SENTRY_DSN}}
${{environment.KEY}} reads a variable of the service's environment. ${{project.KEY}} reads a project variable.
This keeps each value in one place. Rotate a database password by changing the environment variable once, and every service that references it picks up the new value on its next deploy. It also means staging and production can use the same service configuration while pointing at different databases.
When changes apply
Variables are read when a service starts. After changing one, redeploy the service for it to take effect.
Build-time values
Some frameworks read variables during the build, not only at runtime; public keys embedded in a frontend bundle are the usual case. Set those as build arguments in the service's environment settings, so they are available while the image is built.
Per environment
Each environment has its own variables. Production and staging can run the same code with different credentials, and a preview deployment of a pull request gets its own set, so a preview never touches production data.