The supported Docker deployment uses one public origin. The nginx-based web container serves the SPA and forwards the existing API paths to the private API container:
internet -> HTTPS reverse proxy -> web:3000 -> api:3001
|
+-> static SPA
PostgreSQL, migrations, the API, and the worker communicate only on the Compose
network. The default stack does not publish ports 3001, 3003, or 5432.
Copy container.env.example to container.env and set:
APP_URLto the final HTTPS origin, such ashttps://spliit.example.com- a long random
POSTGRES_PASSWORD BETTER_AUTH_SECRETfromopenssl rand -base64 32SMTP_HOST,SMTP_PORT, andEMAIL_FROM- both
SMTP_USERandSMTP_PASSfor authenticated SMTP, or neither for a trusted anonymous relay EMAIL_UNSUBSCRIBE_SECRETfromopenssl rand -hex 32
Then start the project:
docker compose --env-file container.env up -dThe web port binds to 127.0.0.1:3000. Point Caddy, nginx, Traefik, HAProxy, or
another TLS-terminating reverse proxy at that address. The proxy should preserve
the public Host header and set X-Forwarded-For, X-Real-IP, and
X-Forwarded-Proto. Spliit's web gateway preserves those values for the API.
Useful checks:
curl https://spliit.example.com/health/liveness
curl https://spliit.example.com/health/readiness
docker compose --env-file container.env ps
docker compose --env-file container.env logs api workerThe migrate container exiting successfully is expected.
The bundled database image is PostgreSQL 18 and stores its data below
/var/lib/postgresql/<major>/docker; the Compose volume is therefore mounted
at /var/lib/postgresql. If upgrading an existing deployment that used the
legacy /var/lib/postgresql/data mount, do not start the new image until the
database has been migrated. Take a backup first, then use PostgreSQL's
pg_upgrade procedure (or restore a pg_dump into a fresh PostgreSQL 18
volume). A volume containing the old data at the legacy path cannot be reused
by simply changing the image tag.
Change WEB_PORT to move the local web port. Keep BIND_ADDRESS=127.0.0.1
unless the reverse proxy runs on another host or cannot reach host loopback.
The optional API override publishes port 3001:
docker compose \
--env-file container.env \
-f compose.yaml \
-f compose.api-port.yaml \
up -dAPI_BIND_ADDRESS and API_PORT control that mapping. Direct API exposure is
not needed for the normal same-origin setup, including most MCP deployments,
because /auth, /.well-known, and /trpc are already available through the
web origin.
For a separately hosted SPA, build it with VITE_API_URL set to the public API
origin. Set WEB_ORIGINS to the SPA origin and BETTER_AUTH_URL to the API
origin; these override the same-origin APP_URL defaults. This is how the
project can continue serving the web app from Cloudflare Pages while exposing
the API from Dokploy or the API-port Compose override. No runtime config.js is
used.
The example environment file contains every supported toggle. Optional values may remain empty.
- OAuth buttons are enabled automatically when both credentials for Google or GitHub are configured.
- Expense documents require
PUBLIC_ENABLE_EXPENSE_DOCUMENTS=trueand the requiredS3_UPLOAD_*values. Configure the bucket with a lifecycle rule that expires objects undertmp/imports/after 24 hours. Import retries intentionally retain these temporary objects until the database transaction commits; the lifecycle rule removes abandoned browser sessions and interrupted uploads. - AI features require their corresponding
PUBLIC_ENABLE_*flag andAI_API_KEY.AI_PROVIDER, model names, andAI_BASE_URLare optional. - Web Push requires the public key, private key, and subject together.
- MCP requires
ENABLE_MCP=true,MCP_PUBLIC_URL, and a dedicatedASSISTANT_CONFIRMATION_SECRETof at least 32 bytes. Deploy the MCP service separately using the MCP guide.
The background worker is part of the base stack because it handles recurring expenses as well as notifications.
latest follows successful builds from main. Every build is also published
with an immutable commit tag. For controlled upgrades, set SPLIIT_TAG to a
known commit tag before pulling:
docker compose --env-file container.env pull
docker compose --env-file container.env up -dThe one-shot migration service applies pending database migrations before the
new API starts. Back up the database before upgrading. Roll back application
images by restoring the previous SPLIIT_TAG; if a release includes an
incompatible database migration, restore the matching database backup as well.
The postgres_data volume contains the primary application data. Use regular
off-host pg_dump backups or an equivalent PostgreSQL-aware backup system.
Periodically test a full restore into a separate PostgreSQL instance. Object
storage must be backed up separately when expense documents are enabled.
- Terminate HTTPS before the web container and enable HSTS at the outer proxy.
- Do not publish PostgreSQL or the worker health port.
- Leave the API private unless a split-origin deployment requires it.
- Keep
TRUST_PROXY=trueonly when the gateway is reached through a trusted reverse proxy. Set it tofalseif clients connect directly to a gateway bound beyond loopback. - Protect
container.env, database backups, SMTP credentials, and auth secrets. - Configure SPF, DKIM, and DMARC for
EMAIL_FROM. - Pin
SPLIIT_TAGwhen you prefer scheduled upgrades over trackinglatest.