Self-hosting

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.

VariableRequiredWhat it does
DATABASE_URLyesPostgreSQL 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_SECRETyesSecret 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_URLyes in productionThe 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.

VariableRequiredWhat it does
GOOGLE_CLIENT_IDnoOAuth client ID from Google Cloud. Enables the "Continue with Google" button when paired with the secret.
GOOGLE_CLIENT_SECRETnoOAuth 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.

VariableRequiredWhat it does
GROQ_API_KEYnoGroq API key. Enables the "Scan with AI" button on the expense form. Get one at console.groq.com.
GROQ_VISION_MODELnoOverrides the vision model used for scanning. Defaults to meta-llama/llama-4-scout-17b-16e-instruct.
GROQ_BASE_URLnoPoints 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.

VariableRequiredWhat it does
CLERQ_VERSIONnoThe 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.

VariableRequiredWhat it does
CLERQ_CRON_TOKENnoBearer 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_SCHEDULERnoSet 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.

VariableDefaultWhat it does
POSTGRES_USERclerqDatabase role the app connects as.
POSTGRES_PASSWORDclerqDatabase password. Change this before exposing the instance anywhere.
POSTGRES_DBclerqDatabase name.
POSTGRES_PORT5432Host port the database is published on, bound to 127.0.0.1 only.
APP_PORT3000Host 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.

VariableValueWhat it does
NODE_ENVproductionStandard Node production mode.
PORT3000Port the app listens on inside the container.
HOSTNAME0.0.0.0Binds to all interfaces inside the container.
NEXT_TELEMETRY_DISABLED1Disables 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_SECRET and BETTER_AUTH_URL. Everything else has a working default.