Spine reads its settings from .env in the folder you installed it in. Only three are required, and install.sh writes all three. Everything else is optional and degrades instead of failing: without email settings, email waits in a queue; without push keys, browser notifications are hidden.
After changing .env, restart: docker compose --profile minimal up -d.
Most of what an Owner changes day to day — the business profile, tax, numbering, branding, modules, the AI assistant — is in the app under Settings, not here.
| Variable |
What it is |
Without it |
APP_URL |
The address people open Spine at, e.g. https://spine.example.com |
Spine won’t start |
AUTH_SECRET |
A long random secret for sessions and stored keys. install.sh generates it. Keep a copy: restoring a backup on a new machine needs the same one |
Spine won’t start |
DATABASE_URL |
The Postgres connection, postgres://spine:<password>@db:5432/spine |
Spine won’t start |
| Variable |
Default |
What it does |
POSTGRES_PASSWORD |
set by install.sh |
The database’s password; keep it the same as in DATABASE_URL |
API_URL |
<APP_URL>/api |
Where the API is reached from outside (sign-in links, the address for AI agents) |
SPINE_VERSION |
the release’s version |
Which images to run; upgrade.sh sets it |
SITE_ADDRESS |
APP_URL |
What Caddy serves. Set :80 when another proxy in front handles HTTPS (see HTTPS and domains) |
TRUSTED_PROXIES |
127.0.0.1/32 |
Proxies in front of Caddy whose X-Forwarded-For it believes, e.g. private_ranges |
TRUST_PROXY |
loopback and private networks |
Proxies the API believes about a visitor’s address, so rate limits count each visitor; false if nothing is in front of the API |
CLOUDFLARE_TUNNEL_TOKEN |
— |
Remote access through a Cloudflare Tunnel (--profile full) |
| Variable |
Default |
Without it |
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SMTP_FROM |
port 587 |
Documents still issue; emails wait in the queue and send once these are set. See Email |
| Variable |
Default |
What it does |
STORAGE_DRIVER |
local |
local keeps files on the spine_files volume; s3 uses an S3-compatible bucket. See File storage |
FILES_DIR |
/data/files |
Where the local driver keeps files inside the API container |
S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY, S3_SECRET_KEY, S3_REGION |
bucket spine, region us-east-1 |
The bucket for the s3 driver. Without them, uploads are unavailable on s3 |
| Variable |
Default |
What it does |
BACKUP_PASSPHRASE |
— |
Encrypts every backup (AES-256) without asking. Without it, backup.sh --passphrase asks |
BACKUP_REMOTE |
— |
An S3-compatible bucket each encrypted backup is copied to, e.g. s3://my-bucket/spine (keeps 7 daily and 4 weekly) |
BACKUP_REMOTE_ENDPOINT, BACKUP_REMOTE_REGION, BACKUP_REMOTE_ACCESS_KEY, BACKUP_REMOTE_SECRET_KEY |
— |
That bucket’s address and keys |
SNAPSHOTS_DIR |
/data/snapshots |
Where the API keeps a database snapshot before each upgrade’s migrations (the last three) |
| Variable |
Default |
What it does |
CURRENCY |
USD |
The currency a new business starts with (changeable in onboarding) |
LOCALE |
en-US |
Number and date formatting a new business starts with |
TZ |
UTC |
The server’s time zone, for scheduled jobs |
SEED_DEMO |
false |
true or <country>:<industry> loads a demo business on the very first start, into an empty database only |
| Variable |
Default |
Without it |
VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT |
— |
Browser push notifications are hidden; the in-app bell and emails still work. Generate keys with npx web-push generate-vapid-keys |
MOBILE_MIN_VERSION |
— (any version) |
The oldest field app the server accepts; an older one asks to be updated |
| Variable |
Default |
What it does |
UPDATE_CHECK |
on |
off stops the daily check for a new release (no request is made) |
LOG_FORMAT |
json in the images |
json or readable lines |
LOG_REQUESTS |
on |
off stops the one-line-per-request log |
MIGRATE_ON_START |
true |
false leaves database migrations to you |
DATABASE_POOL_MAX, DATABASE_IDLE_TIMEOUT |
the driver’s defaults |
Database connection pool size and idle seconds |