# Configure your app with pyvolt.toml

Everything on your app's Settings tab (the serve command, release steps, Python
version, build step, background workers, and more) can also live in a
`pyvolt.toml` file at the root of your repo. Commit it, and your deploy config
is versioned with your code: reviewed in the same pull request, rolled back
with the same revert, and reproducible on any environment.

This is entirely optional. If your repo has no `pyvolt.toml`, nothing changes:
your app keeps using the settings from the dashboard exactly as before.

## A complete example

```toml
[app]
serve   = "gunicorn config.wsgi:application --workers 2 --bind $SOCK"
release = ["python manage.py migrate --noinput"]
release_env = true              # source the app's env vars into release commands
python  = "3.12"                # optional, see "Versions" below

[build]
command = "npm ci && npm run build"
node    = "20"                  # optional, see "Versions" below

[persist]
dirs = ["data"]                 # paths that must survive a deploy

[processes]
worker = "celery -A config worker"
beat   = "celery -A config beat"

[schedule]
cleanup = { command = "python manage.py clearsessions", cron = "0 3 * * *" }

[frontend]
dir = "dist"
passthrough = ["/api", "/admin"]

[env]
required = ["SECRET_KEY"]       # names only, never values
```

Only include the sections you need. A file with just `[app]` is perfectly
valid.

## How it takes over

When the file is present, the keys it sets **override the dashboard** on every
deploy, and those fields show up read-only on the Settings tab with a "Managed
by pyvolt.toml" badge. The rule is per key:

- A key that is **in the file** is owned by the file. Edit it in the repo.
- A key that is **not in the file** keeps working from the dashboard as usual.

So a file that only sets `[app]` leaves your Processes and Frontend settings
fully editable in the dashboard. Remove a key from the file, deploy, and that
field hands control back to the dashboard.

The file never writes secrets. `[env] required` lists variable *names* only;
the values still live in your app's environment (Settings, or `pyvolt env`).

## The sections

### `[app]`

| Key | What it does |
| --- | --- |
| `serve` | The command that runs your web process. Must bind the socket: include `--bind $SOCK` (gunicorn) or `--uds $SOCK_PATH` (uvicorn). `$APP_MODULE` is also substituted. |
| `release` | A list of commands run on every deploy after the build and before traffic switches over (migrations, `collectstatic`). Runs in the project dir with your venv on `PATH`. |
| `release_env` | `true` to source the app's [environment variables](environment-variables.md) into each release command, so `migrate` can read `DATABASE_URL`. Defaults to `false`: release steps run with a minimal environment. |
| `python` | The Python version, for example `"3.12"`. Optional, see "Versions". |

### `[build]`

| Key | What it does |
| --- | --- |
| `command` | A build step run after dependencies install (for example a front-end build). |
| `node` | The Node version to install for the build, for example `"20"`. Optional, see "Versions". |

### `[persist]`

`dirs` is a list of paths, relative to your project, that must survive a
deploy. Each is stored outside the versioned release and symlinked back in, so
a SQLite database or an uploads folder is not wiped when the next release
replaces the checkout. Use it for anything your app writes to disk at runtime.

### `[processes]`

A table of `name = "command"` background workers. Each becomes a supervised
service that restarts on crash, on reboot, and on every deploy. When this
section is present it defines the **complete** set of workers: the Processes
tab goes read-only, and any worker not listed here (including ones created in
the dashboard) is removed on the next deploy, so the file and the running set
always match. It's one or the other per app: manage processes in the dashboard,
or in the file, not both.

### `[schedule]`

A table of `name = { command = "...", cron = "..." }` [scheduled
jobs](scheduler.md). `cron` is a 5-field expression, validated with the same
rules as the Scheduler tab. Either TOML shape works:

```toml
[schedule]
cleanup = { command = "python manage.py clearsessions", cron = "0 3 * * *" }

[schedule.weekly-report]
command = "python manage.py send_report"
cron    = "0 8 * * 1"
```

Same complete-set rule as `[processes]`: when the section is present it
defines all of the app's scheduled jobs, the Scheduler tab goes read-only
(Run now and View output still work), and any job not listed - including
ones created in the dashboard - is removed on the next deploy.

### `[frontend]`

`dir` is a built static/SPA directory served at the site root, and
`passthrough` is a list of path prefixes (each starting with `/`) that still
proxy to your app. With no `[frontend]` section, everything proxies to the app.

### `[env]`

`required` is a list of environment-variable names your app must have. If any
is missing, the deploy stops immediately with a clear message, before anything
is built. This catches the classic "forgot to set `SECRET_KEY`" mistake early
rather than at runtime.

## Versions

`python` and `node` are optional. Leave a version out (or omit the key
entirely) and the app keeps its current setting:

- No `python` uses the app's Python version, which defaults to **3.12** for a
  new app.
- No `node` uses the repo's own Node version files (`.node-version`,
  `.mise.toml`) if present, otherwise the server's default Node.

Set them only when you want to pin an exact version from the repo.

## Validate before you commit

The deploy validates the file first and stops on any problem: a syntax error, a
key that is not part of the schema (a typo like `serv =`), or a value of the
wrong shape (`release` must be a list, `python` must look like a version). The
error names the exact problem so you can fix it in one pass.

You can run that same check locally with the CLI before you commit:

```bash
pyvolt config validate            # checks ./pyvolt.toml
```

## Starting from your current settings

To turn an app you already configured in the dashboard into a file, export its
current settings and commit the result:

```bash
pyvolt apps export-config my-app --write     # writes ./pyvolt.toml
```

Edit the generated file, commit it, and your next deploy runs from the repo.
