# Configure your application

Add variables, secrets, dependencies, storage, and routes to a Project.

Canonical: https://docs.deliberate.space/build/configuration/

Keep the state needed to run your application below `.deliberate/`:

```text
.deliberate/
├── project.yaml
├── app.yaml
└── storage.yaml
```

`project.yaml` identifies the Team, Project, and Environment Flow. Put the
application resources in any other YAML files you find useful. A file can
contain several documents separated by `---`.

Preview and apply the complete configuration with:

```sh
deliberate diff --env production
deliberate apply --env production
```

## Connect a database, bucket, or volume

Dependencies are explicit. `needs` grants access to a resource and, where
needed, opens the network path from this Component to it. An expression places
one of that resource's outputs where the consuming software expects it:

```yaml
env:
  DATABASE_URL: "${{ postgres.url }}"
needs:
  databases:
    - postgres
```

Your application chooses the environment-variable name. The dependency grants
access; the expression maps the output into the configuration your application
already expects.

For example, an application can use a database, object storage, and a durable
filesystem with the names its libraries already expect:

```yaml
env:
  DATABASE_URL: "${{ postgres.url }}"
  S3_ENDPOINT: "${{ assets.endpoint }}"
  S3_BUCKET: "${{ assets.bucket }}"
  S3_ACCESS_KEY: "${{ assets.access_key }}"
  S3_SECRET_KEY: "${{ assets.secret_key }}"
needs:
  databases:
    - postgres
  buckets:
    - assets
  volumes:
    - name: uploads
      path: /app/uploads
```

Database and bucket credentials remain secret references in the deployed
workload. A volume is mounted only into the Component that asks for it. A
declared dependency that is not referenced in `env` grants access but does not
invent an environment-variable name.

Component outputs use `${{ component.host }}`, `${{ component.port }}`, or
`${{ component.url }}`. Route outputs use `${{ route.url }}` or
`${{ route.host }}`. Blueprint instances expose only the outputs their author
declared. The [YAML reference](/reference/yaml/) contains a complete example.

## Set environment variables

Put non-sensitive configuration directly in `env`:

```yaml
env:
  LOG_LEVEL: info
  FEATURE_SIGNUP: "true"
```

Values are strings and belong to desired state. Commit them with the rest of
the Project, change them, and run `deliberate apply` again. The platform rolls
out the resulting Component revision.

## Add secrets

YAML declares which secrets a Component consumes, but values are stored outside
Git:

```yaml
secrets:
  - SESSION_SECRET
  - OPENAI_API_KEY
```

Set a value through standard input, then apply:

```sh
echo -n "$SESSION_SECRET" | deliberate secret set SESSION_SECRET --env production
deliberate apply --env production
```

Each declared name becomes an environment variable with the same name. To map a
secret to a different variable name, keep it in `secrets` and reference it:

```yaml
env:
  PROVIDER_TOKEN: "${{secrets.OPENAI_API_KEY}}"
secrets:
  - OPENAI_API_KEY
```

Secret lookup is Component → Environment → Project → Team. Use the narrowest
scope that matches the intended sharing boundary. For example, omit scope flags
for a Team-wide value, use `--project hello-java` across its Environments, or
use `--env production --component app` for one deployed consumer. Inspect names
and scopes—not values—with `deliberate secret list`.

Changing a stored value restarts affected consumers so the new process receives
it. Stored values are not returned by the CLI or console. Blueprint secret
arguments follow the same boundary.

## Run migrations and deployment automation

Components can run a bounded `release` command before rollout or a `postDeploy`
command after the new revision is ready. The commands run in separate one-shot
containers and have deliberately different failure behavior.

Use [lifecycle hooks](/build/lifecycle-hooks/) for migrations, cache warming,
and other revision-specific automation.

## Expose the application

Every declared Component port is reachable only by authorized siblings unless
you expose it. Marking an HTTP port `visibility: public` is the shortest path:

```yaml
ports:
  - port: 8080
    protocol: http
    visibility: public
```

The platform creates a generated HTTPS address. Use an explicit `route` when
you want path routing, a chosen generated hostname, a custom domain, or Private
Net access:

```yaml
resource: route
host: app
visibility: public
rules:
  - path: /
    to: web
  - path: /api
    to: api
```

The most specific path wins, so `/api` is selected before `/` regardless of
declaration order. TLS for generated hostnames is platform-owned.

For a customer-owned hostname, declare the hostname and the matching route in
the same Environment:

```yaml
resource: domain
hostname: app.example.com

---
resource: route
host: app.example.com
visibility: public
rules:
  - path: /
    to: web
```

After applying, `deliberate domains show app.example.com --env production`
prints the required DNS records. Once they resolve, run `deliberate domains
verify app.example.com --env production` to verify ownership and start
certificate issuance. Follow [Add a custom domain](/deploy/custom-domains/) for
the complete DNS and verification workflow.

Set `visibility: private` when a route should answer only for permitted Team
members on enrolled devices. `allow` can narrow access to Team roles, handles,
or email addresses. Read [Connect and grant access](/private-net/connect/)
before choosing private-first onboarding: the application may need a public
first-run page before its users have enrolled a device.

See the [YAML reference](/reference/yaml/) for every resource and a complete,
schema-tested example.

## Next steps

- [Add a database](/data/databases/)
- [Add object storage](/data/object-storage/)
- [Attach a custom domain](/deploy/custom-domains/)
