Environment variables
Clerq is configured entirely through environment variables, so the same build runs in development, staging and production. This page lists every variable the app actually reads. If a setting is not on this page, the app does not read it.
When you run the bundled docker-compose.yml, the compose-level variables in
the last section are assembled into the application variables for you - you
generally only set POSTGRES_PASSWORD, BETTER_AUTH_SECRET and
BETTER_AUTH_URL.
Application variables
These are read by the app (and the migration step) at runtime.
| Variable | Required | What it does |
|---|---|---|
DATABASE_URL | yes | PostgreSQL connection string, e.g. postgresql://clerq:password@host:5432/clerq. Read by the app and by drizzle-kit when applying migrations. Under docker-compose this is built for you from the POSTGRES_* values and points at the internal postgres service - you do not set it yourself there. |
BETTER_AUTH_SECRET | yes | Secret key used to sign session cookies and the short-lived signed invoice-PDF download links. Use at least 32 random characters. Rotating it signs everyone out and invalidates any outstanding PDF links. |
BETTER_AUTH_URL | yes in production | The external base URL your instance is reached on, e.g. https://clerq.example.com. Used for authentication callbacks and redirects. It must match the scheme, host and port your users actually use. Defaults to http://localhost:3000 for local runs. |
Generating BETTER_AUTH_SECRET
openssl rand -base64 32
The app logs a warning if the secret is shorter than 32 characters or looks low-entropy. Treat that warning as an error for any instance other people can reach.
Optional: Google sign-in
Google SSO is strictly optional. Self-hosting never requires a third-party account - email and password sign-in always works. The Google button only appears when both variables below are set; leave them unset to hide it.
| Variable | Required | What it does |
|---|---|---|
GOOGLE_CLIENT_ID | no | OAuth client ID from Google Cloud. Enables the "Continue with Google" button when paired with the secret. |
GOOGLE_CLIENT_SECRET | no | OAuth client secret that pairs with the client ID above. |
The authorized redirect URI to register with Google is
<BETTER_AUTH_URL>/api/auth/callback/google. See
Going to production for the full setup.
Optional: AI receipt scanning
AI receipt scanning is strictly optional. When configured, a Scan with AI
button appears on the expense form once you attach a receipt image. Clicking it
sends that image to a Groq vision model, which reads the receipt and pre-fills
the description, amount, currency, vendor, category and notes for you to review
before saving. The button only appears when GROQ_API_KEY is set; leave it
unset to hide the feature entirely.
| Variable | Required | What it does |
|---|---|---|
GROQ_API_KEY | no | Groq API key. Enables the "Scan with AI" button on the expense form. Get one at console.groq.com. |
GROQ_VISION_MODEL | no | Overrides the vision model used for scanning. Defaults to meta-llama/llama-4-scout-17b-16e-instruct. |
GROQ_BASE_URL | no | Points scanning at an OpenAI-compatible proxy or gateway instead of Groq directly. Defaults to https://api.groq.com/openai. |
Privacy. The receipt image is sent to Groq only at the moment a user clicks Scan with AI - never automatically, and never if the key is unset. PNG, JPEG and PDF receipts can all be scanned; PDFs are rasterized to an image in the browser first (their first page), so only an image ever leaves the instance. No expense is saved by scanning; it only fills in the form.
Optional: version and update checks
A self-hosted instance can notice when a newer Clerq release is published and show a dismissible banner. It knows which release it is running from a single build-time value.
| Variable | Required | What it does |
|---|---|---|
CLERQ_VERSION | no | The release string baked into the image at build time (a Docker build argument, e.g. 1.3.7). Official ghcr.io/punterdigital/clerq images set this for you; you do not pass it at runtime. Images with no version - local and development builds - skip update checks entirely. |
Update checks are on by default and can be turned off in Settings -> System. The managed cloud host never checks. See Platform administration for the full behaviour.
Optional: recurring-invoice scheduler
Recurring invoices are driven by a scheduler that sweeps for due schedules. On a normal long-running deployment it runs in-process and needs no configuration. These variables only matter if you run on a host with no long-lived process (serverless, edge) and drive the sweep with an external cron instead.
| Variable | Required | What it does |
|---|---|---|
CLERQ_CRON_TOKEN | no | Bearer token that guards the POST /api/cron/run sweep endpoint. The endpoint returns 404 until this is set, so it is never open on a default install. Point an external scheduler at it (Authorization: Bearer <token>) to trigger the sweep; running it alongside the in-process ticker is harmless, as the sweep is idempotent. |
CLERQ_DISABLE_SCHEDULER | no | Set to 1 to turn the in-process scheduler off - for deployments that drive the sweep purely through /api/cron/run. |
Compose-level variables
These are read by the bundled docker-compose.yml itself (not by the app
directly). Compose uses them to build the database, bind host ports, and
construct DATABASE_URL. Set them in the .env file next to the compose
file.
| Variable | Default | What it does |
|---|---|---|
POSTGRES_USER | clerq | Database role the app connects as. |
POSTGRES_PASSWORD | clerq | Database password. Change this before exposing the instance anywhere. |
POSTGRES_DB | clerq | Database name. |
POSTGRES_PORT | 5432 | Host port the database is published on, bound to 127.0.0.1 only. |
APP_PORT | 3000 | Host port mapped to the app container's port 3000. |
Set automatically by the image
The production image sets these for you. You normally never change them, but they are listed here so nothing is hidden.
| Variable | Value | What it does |
|---|---|---|
NODE_ENV | production | Standard Node production mode. |
PORT | 3000 | Port the app listens on inside the container. |
HOSTNAME | 0.0.0.0 | Binds to all interfaces inside the container. |
NEXT_TELEMETRY_DISABLED | 1 | Disables Next.js telemetry. |
What Clerq does not need
To save you searching for settings that do not exist:
- No SMTP or email configuration. Clerq sends no email. Team invitations are shareable links you send yourself, and invoices are exported as PDFs rather than emailed. There is no mail server to configure.
- No currency-API key. Exchange rates come from the European Central Bank's public reference rates, fetched server-side with no key and no account.
- No object storage. Logos and expense receipts are stored inline in the database, so there is no S3 bucket or blob store to run.
- No sign-up toggle today. There is no environment variable to disable new registrations. If your instance is internet-facing and you do not want open sign-ups, put it behind your own access control - see Going to production.
The shortest viable production config is three lines:
POSTGRES_PASSWORD,BETTER_AUTH_SECRETandBETTER_AUTH_URL. Everything else has a working default.