Documentation menu▾

Configuration

Every environment variable Keygate reads, what it does and its default: database, secrets, Stripe, email, Redis, rate limits, webhooks and release storage.

How settings are read

Keygate reads its settings from environment variables. When it starts, it also loads a .env file from its working folder, and real environment variables win over the file. With Docker Compose, everything in .env is passed to the container.

A few things, such as the email provider, the site name and which emails go out, can also be changed later in the dashboard under Settings. Those are stored in the database and do not need a restart.

Keygate checks its settings at startup and refuses to start if a required one is missing or clearly wrong, such as a signing key of the wrong length. The log says which one.

Server

VariableDefaultWhat it does
PORT9000Port the HTTP server listens on.
BASE_URLhttp://localhost:9000The public address of your install. Used in email links, after Stripe Checkout, as the Stripe webhook address and in the update feed addresses the dashboard shows. Use HTTPS in production.
ENVIRONMENTdevelopmentdevelopment, staging or production. Development turns on a login shortcut without email codes, meant for local work only. Production warns at startup when BASE_URL is not HTTPS or ADMIN_EMAILS is empty.
DATABASE_URLrequiredPostgreSQL connection string, for example postgres://keygate:secret@db:5432/keygate?sslmode=disable.

Secrets

Generate each secret with openssl rand -hex 32. Store them somewhere safe; the backups section explains what happens if one is lost.

VariableDefaultWhat it does
JWT_SECRETrequiredSigns login sessions. At least 32 characters.
LICENSE_SIGNING_KEYrequiredEd25519 key that signs the offline tokens your app verifies. Exactly 64 hex characters.
RELEASE_KEY_ENCRYPTION_KEYemptyEncrypts release signing keys, email provider credentials and stored license keys. Required when release storage is on, and to save SMTP or Cloudflare credentials in the dashboard.

Stripe

VariableDefaultWhat it does
STRIPE_SECRET_KEYemptyYour Stripe secret or restricted key. When set, Keygate creates its own webhook endpoint in Stripe, so nothing else is needed.
STRIPE_WEBHOOK_SECRETemptyOnly needed when you forward events yourself, for example with the Stripe CLI during development. See local development.

Email

Without email settings Keygate still runs, and login codes are written to the server log. You can set SMTP here or pick a provider in the dashboard. More in Emails.

VariableDefaultWhat it does
SMTP_HOSTemptySMTP server host name.
SMTP_PORT587SMTP port.
SMTP_USERNAMEemptySMTP user.
SMTP_PASSWORDemptySMTP password.
SMTP_FROMemptySender address, either [email protected] or Acme Licensing <[email protected]>.

Redis

VariableDefaultWhat it does
REDIS_URLemptyRedis or Valkey address, such as redis://localhost:6379/0. Shares rate limits across instances. If Redis goes away, Keygate falls back to counting in memory until it is back.

Rate limits

Limits are per client IP address.

VariableDefaultWhat it does
RATE_LIMIT_API60Base rate per minute. The license API allows twice this, at least 120, and update feeds four times, at least 240.
RATE_LIMIT_ADMIN120Requests per minute to the admin API.
RATE_LIMIT_AUTH60Login attempts per minute. People behind one office network share it.
RATE_LIMIT_OTP_SEND30Login codes sent per hour. Kept low because this endpoint mails any address it is given.
BF_MAX_FAILS5Failed license calls from one address within five minutes before it is locked out.
BF_LOCKOUT_SECONDS30The first lockout. Each one after that lasts twice as long, up to 30 minutes.

Webhooks

VariableDefaultWhat it does
WEBHOOK_MAX_ATTEMPTS5Delivery attempts before giving up.
WEBHOOK_RETRY_INTERVAL30sHow often Keygate looks for deliveries that are due for a retry.
WEBHOOK_HTTP_TIMEOUT10sHow long to wait for your endpoint.
WEBHOOK_ALLOW_PRIVATEfalseAllow deliveries to private and local addresses, such as a receiver on the same Docker network.

Release storage

Only needed if Keygate hosts your app's releases. Leave STORAGE_BUCKET empty to turn releases off; everything else works without it. The bucket, access key and secret key must be set together. See Shipping updates.

VariableDefaultWhat it does
STORAGE_ENDPOINTemptyEmpty for AWS S3, https://ACCOUNT.r2.cloudflarestorage.com for R2, http://minio:9000 for MinIO.
STORAGE_REGIONautoauto for R2, the bucket's region for S3.
STORAGE_BUCKETemptyBucket name.
STORAGE_ACCESS_KEYemptyAccess key.
STORAGE_SECRET_KEYemptySecret key.
STORAGE_FORCE_PATH_STYLEfalseSet to true for MinIO and most self hosted S3 gateways.
STORAGE_UPLOAD_TTL1hHow long an upload link stays valid.
STORAGE_DOWNLOAD_TTL10mHow long a license checked download link stays valid.
STORAGE_FEED_URL_TTL24hHow long download links inside update feeds stay valid. Users may click Install long after the feed was fetched.
MAX_RELEASE_SIGN_SIZE_MB500Largest file Keygate signs. Signing holds the whole file in memory.

Other settings

VariableDefaultWhat it does
ADMIN_EMAILSemptyComma separated emails that become admins the first time they sign in.
QUOTA_WARNING_THRESHOLD0.8Share of a usage quota at which the warning webhook and email go out.
.env
BASE_URL=https://licenses.example.com
JWT_SECRET=2f1c...c9a0
LICENSE_SIGNING_KEY=8be4...41d7
RELEASE_KEY_ENCRYPTION_KEY=5a90...e3b2
STRIPE_SECRET_KEY=sk_live_...
SMTP_HOST=smtp.example.com
[email protected]
SMTP_PASSWORD=...
SMTP_FROM=Acme Licensing <[email protected]>

Last updated October 4, 2026