Skip to main content
Environment variables let you configure Primo for different deployment environments. These are particularly useful for Docker deployments and automated setup.
Legacy PALA_* variable names are no longer read. If an older deployment still sets PALA_SUPERUSER_EMAIL or similar, rename those variables to their PRIMO_* equivalents below; otherwise they’re silently ignored.

Initial Setup Variables

These variables only take effect on first startup when initializing a new instance:

PRIMO_SUPERUSER_EMAIL

Creates the initial PocketBase superuser for the backend dashboard at /_/. This is separate from the regular user account you use to sign in to Primo.
  • Type: String (email address)
  • Required: No
  • Default: None
  • When to use: Automated deployments, skipping manual setup
Example:

PRIMO_SUPERUSER_PASSWORD

Password for the initial superuser account.
  • Type: String
  • Required: Only if PRIMO_SUPERUSER_EMAIL is set
  • Default: None
  • Minimum: 8 characters
Example:
If both PRIMO_SUPERUSER_EMAIL and PRIMO_SUPERUSER_PASSWORD are set, the setup screen will be skipped and you’ll go straight to the sign-in page.

PRIMO_USER_EMAIL

Creates an initial regular user account.
  • Type: String (email address)
  • Required: No
  • Default: None
  • When to use: Create the initial Primo administrator. This account receives the server Developer role; it is not a restricted Content Editor.
Example:

PRIMO_USER_PASSWORD

Password for the initial regular user account.
  • Type: String
  • Required: Only if PRIMO_USER_EMAIL is set
  • Default: None
  • Minimum: 8 characters
Example:

PRIMO_APP_URL

Sets the base URL for the application. Used for generating links in emails and backend processes.
  • Type: String (URL)
  • Required: No
  • Default: PocketBase’s configured application URL; set the public HTTPS URL explicitly for automated setup
  • When to use: Email notifications, API callbacks
Example:

Custom Domain Variables

These control how sites get a public address: an automatic subdomain on creation, and connecting a site’s own custom domain.

PRIMO_BASE_DOMAIN

A base domain used to assign new sites a subdomain (<site-slug>.<base-domain>) when they are created. The address works publicly only after you configure wildcard DNS and TLS. Without it, new sites start unassigned (editable but not publicly served) until a domain is connected.
  • Type: String (domain)
  • Required: No
  • Default: Empty (new sites start unassigned)
  • When to use: Multi-site instances where every site should be reachable immediately without manual DNS.
Setting this variable only tells Primo which subdomains to assign. For those subdomains to actually resolve and serve over HTTPS, you must separately point a wildcard DNS record (*.<base-domain>) at this server and provision a matching wildcard TLS certificate. Primo does not create the DNS record or the certificate.
Example:

PRIMO_DOMAIN_PROVIDER

Selects how connecting a site’s own custom domain works. manual (the default) shows the DNS records to add and leaves routing/TLS to your reverse proxy; railway attaches the domain through Railway’s API and reports certificate status back to the editor.
  • Type: String (manual | railway)
  • Required: No
  • Default: manual
  • When to use: Set to railway when the instance runs on Railway and you want custom domains attached automatically.
Example:

PRIMO_RAILWAY_TOKEN

A Railway project access token scoped to the same environment as Primo, used only when PRIMO_DOMAIN_PROVIDER is railway. Create it in the Railway dashboard under the project’s Settings → Tokens (environment-scoped within the project; use the least-privileged token). A token for a different environment causes domain provisioning to fail. The project/environment/service IDs Railway injects automatically (RAILWAY_PROJECT_ID, etc.) supply the rest.
  • Type: String (token)
  • Required: Only when PRIMO_DOMAIN_PROVIDER=railway
  • Default: Empty
Example:

Operations Variables

These adjust built-in safeguards. The defaults suit most deployments.

PRIMO_RATE_LIMIT

Rate limiting is on by default. Signed-out requests are limited, including repeated sign-in attempts, which gives brute-force protection. Signed-in editors aren’t throttled.
  • Type: off to disable
  • Required: No
  • Default: Enabled
  • When to use: Your own proxy or firewall already rate-limits requests
Example:
This is read once: on the first start of a new instance, or the first start after upgrading to 3.2.7 or later. After that, change rate limits in the PocketBase admin UI (/_/ → Settings → Rate limits).

PRIMO_BACKUP_CRON

Schedule for automatic database backups, as a cron expression. Backups are stored on the data volume in pb_data/backups/, and the 7 most recent are kept.
  • Type: Cron expression
  • Required: No
  • Default: 0 3 * * * (daily at 03:00)
Example:
This is read only on the first start of a new instance. After that, manage the schedule in the PocketBase admin UI (/_/ → Settings → Backups). Backups on the same volume protect against mistakes, not against losing the volume; for off-site copies, configure S3 storage there too.

PRIMO_ENABLE_USAGE_STATS

Controls CMS usage analytics, including server heartbeats and editor events. In the v3.3.1 source, self-hosted instances default to off and hosted mode defaults to on. The variable overrides this default when set.
  • Type: true or false
  • Required: No
  • Default: false for self-hosted instances; true in hosted mode (PRIMO_HOSTED_MODE=true) in the v3.3.1 source
Example:
Set this to false to opt out, then restart or redeploy your CMS service. Use false explicitly when analytics must stay off. Verify the effective setting with curl -fsS https://cms.example.com/api/primo/info and check telemetry_enabled; do not rely only on the deployment mode. See Usage analytics in the self-hosting guide for a short overview.

CORS_ALLOWED_ORIGINS

Controls the custom CORS middleware for requests from another origin. Leave it unset for ordinary same-origin CMS use. For an external dashboard, set a comma-separated list of complete origins, including the scheme and any port.
The value * allows any origin without credentials and is intended for public marketplace access. PRIMO_APP_URL does not configure CORS. Restart the service after changing this setting.

Common Configurations

Development Setup

The setup screen will guide you through creating your first account.

Production with Manual Setup

Access the setup screen to create your admin account manually.

Production with Automated Setup

This example creates both accounts. Sign in to Primo with PRIMO_USER_EMAIL; use PRIMO_SUPERUSER_EMAIL for the PocketBase dashboard.

Docker Compose Example

Set SUPERUSER_PASSWORD and USER_PASSWORD to distinct generated passwords in a private .env file beside docker-compose.yml. Keep it out of version control. Compose stops if either password is missing.

Railway Template

Railway automatically handles environment variables through their UI:
  1. Deploy using the Railway button
  2. Set environment variables in Railway dashboard
  3. Apply the staged variable changes and redeploy the service

Security Best Practices

Use environment variables, secrets management, or encrypted files. Never hardcode passwords in docker-compose.yml or other config files.
Generate random passwords with at least 16 characters:
Change initial passwords after setup, especially for production environments.
For production deployments, use:
  • Railway: Built-in secrets
  • Docker Swarm: Docker secrets
  • Kubernetes: Kubernetes secrets
  • Vault: HashiCorp Vault
Environment variables contain sensitive information. Restrict access to deployment configurations.

Troubleshooting

Setup screen appears even with account variables set

Both email and password must be present when the initialization migration runs. Check container logs for validation errors and confirm the variables reached the service. Existing databases do not re-run account creation when you change these variables. To recover PocketBase access without deleting data, see Locked out of admin. In PocketBase, create or update a regular users record with the intended server role for CMS access.

Account variables do not update existing passwords

Change an existing user’s password through PocketBase or the password-reset workflow. Changing PRIMO_USER_PASSWORD or PRIMO_SUPERUSER_PASSWORD after initialization does not rotate the account password. You do not need to delete a database or Docker volume to recover access.

Next Steps

Deployment Guide

Learn how to deploy Primo to production

Troubleshooting

Solve common deployment issues