Skip to content

Environment variables

This page lists every setting Goiabada’s two servers read, and what each one does.

Goiabada runs as two servers, the auth server (goiabada-authserver) and the admin console (goiabada-adminconsole), and you configure both with environment variables. Most variables also have a command-line flag, and the flag wins when you set both:

Terminal window
GOIABADA_AUTHSERVER_LOG_LEVEL=debug ./goiabada-authserver
./goiabada-authserver --authserver-log-level=debug

You rarely need to start from scratch: the setup wizard writes a working set for your deployment, keys included.

Read by says which server reads a variable. A server ignores the variables it doesn’t read, so you can hand both servers the same environment.

The auth server reads these when it starts on an empty database, to create the first administrator and the admin console’s client. See the first start.

Variable What it does
GOIABADA_ADMIN_EMAIL
Flag: --admin-email
Default: [email protected]
Read by: auth server
The first administrator’s email address.
GOIABADA_ADMIN_PASSWORD
Flag: --admin-password
Default: empty
Read by: auth server
The first administrator’s password. Required on the first start: at least 15 characters and at most 72 bytes.
GOIABADA_APPNAME
Flag: --appname
Default: Goiabada
Read by: auth server
The name the UI shows. Change it later under Admin, General in the admin console.
GOIABADA_ADMINCONSOLE_OAUTH_CLIENT_SECRET
Flag: --adminconsole-oauth-client-secret
Default: empty
Read by: both
The admin console’s client secret. The admin console needs it to call the auth server’s API, and refuses to start when the auth server refuses it. The auth server reads it only on the first start, as the secret of the client it creates for the admin console.
GOIABADA_AUTHSERVER_BOOTSTRAP_ENV_OUTFILE
Flag: --authserver-bootstrap-env-outfile
Default: empty
Read by: auth server
A file for the two-step first start. The auth server writes the credentials it generated there, then exits.

Only the auth server talks to the database. The admin console reaches your data through the auth server’s API.

Variable What it does
GOIABADA_DB_TYPE
Flag: --db-type
Default: sqlite
Read by: auth server
The database engine: sqlite, mysql, postgres or mssql.
GOIABADA_DB_HOST
Flag: --db-host
Default: localhost
Read by: auth server
The database host. An IPv6 address works with or without brackets.
GOIABADA_DB_PORT
Flag: --db-port
Default: 3306
Read by: auth server
The database port.
GOIABADA_DB_NAME
Flag: --db-name
Default: goiabada
Read by: auth server
The database name.
GOIABADA_DB_USERNAME
Flag: --db-username
Default: root
Read by: auth server
The database user.
GOIABADA_DB_PASSWORD
Flag: --db-password
Default: empty
Read by: auth server
The database user’s password.
GOIABADA_DB_TLS_MODE
Flag: --db-tls-mode
Default: prefer
Read by: auth server
How the auth server protects its connection to MySQL, PostgreSQL or SQL Server: disable, prefer, require, verify-ca or verify-full. Only verify-full checks that it reached your own database: Where the database sits compares the five. prefer checks no certificate, and encrypts when the server offers TLS; on SQL Server it encrypts the login, and the rest only when the server forces encryption. Left unset, it’s prefer, and every start writes a warning. SQLite ignores it.
GOIABADA_DB_TLS_CA_FILE
Flag: --db-tls-ca-file
Default: empty
Read by: auth server
A PEM file of the authorities you trust to sign the database’s certificate. It’s read once at start, in verify-ca and verify-full only, and trusted in place of the system’s authorities. The auth server won’t start if the file can’t be read, holds no certificate, or is set with another mode. SQLite ignores it.
GOIABADA_DB_TLS_CLIENT_CERT_FILE
Flag: --db-tls-client-cert-file
Default: empty
Read by: auth server
A PEM file of the client certificate the auth server presents to a database that authenticates by certificate. Name its key in GOIABADA_DB_TLS_CLIENT_KEY_FILE. It’s read once at start, and presented in require, verify-ca and verify-full on PostgreSQL and MySQL: SQL Server’s driver can’t present one. SQLite ignores it. See A client certificate.
GOIABADA_DB_TLS_CLIENT_KEY_FILE
Flag: --db-tls-client-key-file
Default: empty
Read by: auth server
A PEM file of the client certificate’s private key, beside GOIABADA_DB_TLS_CLIENT_CERT_FILE. It must not be encrypted: the auth server reads no passphrase.
GOIABADA_DB_TLS_SERVER_NAME
Flag: --db-tls-server-name
Default: empty
Read by: auth server
The host name verify-full checks the database’s certificate against, in place of GOIABADA_DB_HOST, when the address dialed isn’t a name the certificate carries: an IP address, a pooler or a private endpoint. Set it only with verify-full, on MySQL, PostgreSQL or SQL Server. SQLite ignores it.
GOIABADA_DB_CREATE
Flag: --db-create
Default: true
Read by: auth server
Create the database if it doesn’t exist. Set it to false when you create the database yourself.
GOIABADA_DB_DSN
Flag: --db-dsn
Default: file::memory:?cache=shared
Read by: auth server
The SQLite connection string. The default keeps everything in memory, so it’s gone when the server stops. See SQLite.
GOIABADA_DB_MAX_OPEN_CONNS
Flag: --db-max-open-conns
Default: 20
Read by: auth server
The most connections open at once. At least 1.
GOIABADA_DB_MAX_IDLE_CONNS
Flag: --db-max-idle-conns
Default: 20
Read by: auth server
The most idle connections kept for reuse, from 0 up to the open limit. Unset, it follows GOIABADA_DB_MAX_OPEN_CONNS.
GOIABADA_DB_CONN_MAX_LIFETIME
Flag: --db-conn-max-lifetime
Default: 30m
Read by: auth server
How long a connection is reused before it’s replaced. 0 means no limit.
GOIABADA_DB_CONN_MAX_IDLE_TIME
Flag: --db-conn-max-idle-time
Default: 5m
Read by: auth server
How long a connection can sit idle before it’s closed. 0 means no limit.

The last four size the connection pool.

A base URL is the address browsers and clients use. A listener is where the server accepts connections on its own machine, which can be quite different behind a proxy.

Variable What it does
GOIABADA_AUTHSERVER_BASEURL
Flag: --authserver-baseurl
Default: http://localhost:9090
Read by: both
The auth server’s public URL. The first start also makes it the issuer that tokens carry. An https:// URL marks the auth server’s cookies Secure.
GOIABADA_AUTHSERVER_INTERNALBASEURL
Flag: --authserver-internalbaseurl
Default: empty
Read by: both
Where the admin console calls the auth server’s API, such as http://goiabada-authserver:9090 inside a cluster. Empty, it uses the public URL. The auth server only reports it in its startup log.
GOIABADA_ADMINCONSOLE_INTERNAL_CA_FILE
Flag: --adminconsole-internal-ca-file
Default: empty
Read by: admin console
A PEM file of the certificate authorities you trust to sign the auth server’s certificate, on the https:// URL the admin console calls it at. They’re trusted beside the system’s authorities, not in place of them. It’s read once, at start.
GOIABADA_ADMINCONSOLE_BASEURL
Flag: --adminconsole-baseurl
Default: http://localhost:9091
Read by: both
The admin console’s public URL. The auth server links to it in its emails and sends visitors of / there. An https:// URL marks the admin console’s cookies Secure. The auth server’s first start also registers it, and it followed by /auth/callback, as the admin console client’s redirect URIs. To change it later, first add the new URL, and it followed by /auth/callback, on the client’s Redirect URIs tab: https://, or http on loopback.
GOIABADA_AUTHSERVER_LISTEN_HOST_HTTP
Flag: --authserver-listen-host-http
Default: 0.0.0.0
Read by: auth server
The address the auth server’s HTTP listener binds. Set it empty to turn that listener off. See listen addresses.
GOIABADA_AUTHSERVER_LISTEN_PORT_HTTP
Flag: --authserver-listen-port-http
Default: 9090
Read by: auth server
The auth server’s HTTP port.
GOIABADA_AUTHSERVER_LISTEN_HOST_HTTPS
Flag: --authserver-listen-host-https
Default: 0.0.0.0
Read by: auth server
The address the auth server’s HTTPS listener binds. It listens only once the certificate and key files are set.
GOIABADA_AUTHSERVER_LISTEN_PORT_HTTPS
Flag: --authserver-listen-port-https
Default: 9443
Read by: auth server
The auth server’s HTTPS port.
GOIABADA_AUTHSERVER_CERTFILE
Flag: --authserver-certfile
Default: empty
Read by: auth server
The TLS certificate file for the auth server’s HTTPS listener. Behind a proxy that handles TLS, you don’t need it.
GOIABADA_AUTHSERVER_KEYFILE
Flag: --authserver-keyfile
Default: empty
Read by: auth server
The TLS private key file for the auth server’s HTTPS listener.
GOIABADA_ADMINCONSOLE_LISTEN_HOST_HTTP
Flag: --adminconsole-listen-host-http
Default: 0.0.0.0
Read by: admin console
The address the admin console’s HTTP listener binds. Set it empty to turn that listener off.
GOIABADA_ADMINCONSOLE_LISTEN_PORT_HTTP
Flag: --adminconsole-listen-port-http
Default: 9091
Read by: admin console
The admin console’s HTTP port.
GOIABADA_ADMINCONSOLE_LISTEN_HOST_HTTPS
Flag: --adminconsole-listen-host-https
Default: 0.0.0.0
Read by: admin console
The address the admin console’s HTTPS listener binds. It listens only once the certificate and key files are set.
GOIABADA_ADMINCONSOLE_LISTEN_PORT_HTTPS
Flag: --adminconsole-listen-port-https
Default: 9444
Read by: admin console
The admin console’s HTTPS port.
GOIABADA_ADMINCONSOLE_CERTFILE
Flag: --adminconsole-certfile
Default: empty
Read by: admin console
The TLS certificate file for the admin console’s HTTPS listener.
GOIABADA_ADMINCONSOLE_KEYFILE
Flag: --adminconsole-keyfile
Default: empty
Read by: admin console
The TLS private key file for the admin console’s HTTPS listener.
Variable What it does
GOIABADA_AUTHSERVER_TRUST_PROXY_HEADERS
Flag: --authserver-trust-proxy-headers
Default: false
Read by: auth server
Take the client’s IP address from X-Forwarded-For or X-Real-IP. Turn it on behind a reverse proxy, and leave it off without one. See client IP addresses.
GOIABADA_AUTHSERVER_TRUSTED_PROXIES
Flag: --authserver-trusted-proxies
Default: empty
Read by: auth server
Your proxies’ addresses, as comma-separated IP addresses or CIDR ranges, such as 10.0.0.0/8,192.168.1.5.
GOIABADA_ADMINCONSOLE_TRUST_PROXY_HEADERS
Flag: --adminconsole-trust-proxy-headers
Default: false
Read by: admin console
The same switch for the admin console, which records the address in its logs.
GOIABADA_ADMINCONSOLE_TRUSTED_PROXIES
Flag: --adminconsole-trusted-proxies
Default: empty
Read by: admin console
The same list for the admin console.

Each of these is random bytes written as hex. Generate them as Session keys and the data encryption key show. They have no flags, so they stay out of process listings.

Variable What it does
GOIABADA_AUTHSERVER_SESSION_AUTHENTICATION_KEY
Flag: none
Default: empty
Read by: auth server
Required. 64 bytes that seal the auth server’s sessions.
GOIABADA_AUTHSERVER_SESSION_ENCRYPTION_KEY
Flag: none
Default: empty
Read by: auth server
Required. 32 bytes that encrypt the auth server’s sessions.
GOIABADA_AUTHSERVER_SESSION_AUTHENTICATION_KEY_PREVIOUS
Flag: none
Default: empty
Read by: auth server
The old authentication key, set only while you rotate the session keys.
GOIABADA_AUTHSERVER_SESSION_ENCRYPTION_KEY_PREVIOUS
Flag: none
Default: empty
Read by: auth server
The old encryption key, set only while you rotate the session keys.
GOIABADA_ADMINCONSOLE_SESSION_AUTHENTICATION_KEY
Flag: none
Default: empty
Read by: admin console
Required. 64 bytes that seal the admin console’s sessions.
GOIABADA_ADMINCONSOLE_SESSION_ENCRYPTION_KEY
Flag: none
Default: empty
Read by: admin console
Required. 32 bytes that encrypt the admin console’s sessions.
GOIABADA_ADMINCONSOLE_SESSION_AUTHENTICATION_KEY_PREVIOUS
Flag: none
Default: empty
Read by: admin console
The old authentication key, set only while you rotate the session keys.
GOIABADA_ADMINCONSOLE_SESSION_ENCRYPTION_KEY_PREVIOUS
Flag: none
Default: empty
Read by: admin console
The old encryption key, set only while you rotate the session keys.
GOIABADA_AES_ENCRYPTION_KEY
Flag: none
Default: empty
Read by: auth server
Required. 32 bytes that encrypt secrets in the database.
GOIABADA_AES_ENCRYPTION_KEY_PREVIOUS
Flag: none
Default: empty
Read by: auth server
The old data encryption key, set only while you rotate it.

Both servers write their logs to standard error.

Variable What it does
GOIABADA_AUTHSERVER_LOG_LEVEL
Flag: --authserver-log-level
Default: info
Read by: auth server
The lowest level written: debug, info, warn or error. debug adds a few diagnostic records; for one record per request, turn on GOIABADA_AUTHSERVER_LOG_HTTP_REQUESTS.
GOIABADA_AUTHSERVER_LOG_FORMAT
Flag: --authserver-log-format
Default: text
Read by: auth server
text for one key=value line per record, or json for one JSON object per record.
GOIABADA_AUTHSERVER_LOG_HTTP_REQUESTS
Flag: --authserver-log-http-requests
Default: false
Read by: auth server
Write one record per request, except health checks, static files and the favicon. Query values are redacted, apart from a short list of safe ones.
GOIABADA_AUTHSERVER_LOG_SQL
Flag: --authserver-log-sql
Default: false
Read by: auth server
Write one record per SQL statement, with its text but not the values it ran with.
GOIABADA_AUTHSERVER_DEBUG_API_REQUESTS
Flag: --authserver-debug-api-requests
Default: false
Read by: auth server
Write one record per API call, with its method, target, status, duration and both bodies. Credentials in the bodies and the Authorization header are redacted.
GOIABADA_ADMINCONSOLE_LOG_LEVEL
Flag: --adminconsole-log-level
Default: info
Read by: admin console
The lowest level written: debug, info, warn or error.
GOIABADA_ADMINCONSOLE_LOG_FORMAT
Flag: --adminconsole-log-format
Default: text
Read by: admin console
text or json, as for the auth server.
GOIABADA_ADMINCONSOLE_LOG_HTTP_REQUESTS
Flag: --adminconsole-log-http-requests
Default: false
Read by: admin console
Write one record per request, as the auth server’s switch does.

A level or format spelled any other way, uppercase included, stops the server at start. To set the time zone of the timestamps, see time zone.

Each server can serve Prometheus metrics on a listener of its own, which answers GET /metrics and nothing else. It has no authentication, so never publish its port to the internet. Monitoring covers what it reports.

Variable What it does
GOIABADA_AUTHSERVER_METRICS_ENABLED
Flag: --authserver-metrics-enabled
Default: false
Read by: auth server
Serve the auth server’s metrics listener. Off, nothing listens on its port.
GOIABADA_AUTHSERVER_LISTEN_HOST_METRICS
Flag: --authserver-listen-host-metrics
Default: 0.0.0.0
Read by: auth server
The address the auth server’s metrics listener binds. Use 127.0.0.1 when the scraper runs on the same host.
GOIABADA_AUTHSERVER_LISTEN_PORT_METRICS
Flag: --authserver-listen-port-metrics
Default: 9190
Read by: auth server
The auth server’s metrics port.
GOIABADA_ADMINCONSOLE_METRICS_ENABLED
Flag: --adminconsole-metrics-enabled
Default: false
Read by: admin console
Serve the admin console’s metrics listener. Off, nothing listens on its port.
GOIABADA_ADMINCONSOLE_LISTEN_HOST_METRICS
Flag: --adminconsole-listen-host-metrics
Default: 0.0.0.0
Read by: admin console
The address the admin console’s metrics listener binds.
GOIABADA_ADMINCONSOLE_LISTEN_PORT_METRICS
Flag: --adminconsole-listen-port-metrics
Default: 9191
Read by: admin console
The admin console’s metrics port.
Variable What it does
GOIABADA_AUTHSERVER_STATICDIR
Flag: --authserver-staticdir
Default: empty
Read by: auth server
A directory to serve the auth server’s static files from, instead of the built-in ones. See Change the templates.
GOIABADA_AUTHSERVER_TEMPLATEDIR
Flag: --authserver-templatedir
Default: empty
Read by: auth server
A directory to load the auth server’s page templates from, instead of the built-in ones.
GOIABADA_ADMINCONSOLE_STATICDIR
Flag: --adminconsole-staticdir
Default: empty
Read by: admin console
The same, for the admin console’s static files.
GOIABADA_ADMINCONSOLE_TEMPLATEDIR
Flag: --adminconsole-templatedir
Default: empty
Read by: admin console
The same, for the admin console’s page templates.
GOIABADA_I18N_OVERRIDES_DIR
Flag: none
Default: empty
Read by: both
A directory whose catalogs subfolder holds active.<language>.toml files, merged key by key over the built-in text. Without a catalogs subfolder nothing is loaded. Set it on both servers. See Add a language.
GOIABADA_PROFILE_PICTURE_MAX_SIZE_BYTES
Flag: none
Default: 3145728
Read by: auth server
The largest profile picture or client logo the API accepts, in bytes. The default is 3 MiB. The admin console’s upload form stops at 3 MiB, whatever this says.
Variable What it does
GOIABADA_AUTHSERVER_RATELIMITER_ENABLED
Flag: --authserver-ratelimiter-enabled
Default: false
Read by: auth server
Turn on the rate limits below that are counted by an IP address. Those counted by a user or an email apply whether or not it’s on. The setup wizard asks whether you want them.

The auth server limits these endpoints. The limits counted by a user or an email always apply, and GOIABADA_AUTHSERVER_RATELIMITER_ENABLED adds the limits counted by an IP address: the Applies column says always or switch on. A refused request gets 429 Too Many Requests, in the format the endpoint usually answers with. The limiter is the name the rate_limit_exceeded audit event and the goiabada_rate_limit_refusals_total metric give it.

Endpoint Limit Counts Counted by Applies
POST /auth/pwd (password sign-in)
Limiter: pwd_account_net
10 per 15 minutes failed sign-ins IP address and email, shared with the password grant switch on
POST /auth/pwd
Limiter: pwd_account
100 per 60 minutes failed sign-ins email, shared with the password grant always
POST /auth/pwd
Limiter: pwd_ip
30 per minute failed sign-ins IP address switch on
POST /auth/pwd
Limiter: pwd_ip_requests
120 per minute every request IP address switch on
POST /auth/otp (authenticator code)
Limiter: otp
5 per 15 minutes failed codes user always
POST /api/v1/account/email/verification
Limiter: email_verification
5 per 15 minutes failed codes the token’s user always
POST /api/v1/account/email/verification/send
Limiter: email_verification_send
5 per 60 minutes every request the token’s user always
PUT /api/v1/account/password, PUT /api/v1/account/otp and PUT /api/v1/account/email
Limiter: account_password
5 per 15 minutes, for the three together failed passwords the token’s user always
POST /forgot-password
Limiter: forgot_pwd_email
5 per 5 minutes every request email always
POST /forgot-password
Limiter: forgot_pwd_ip
20 per 5 minutes every request IP address switch on
GET /reset-password and POST /reset-password
Limiter: reset_pwd
30 per 5 minutes, for the two together every request IP address switch on
POST /account/register (self-registration)
Limiter: register_email
5 per 5 minutes every request email always
POST /account/register
Limiter: register
20 per 5 minutes every request IP address switch on
GET /account/activate and POST /account/activate
Limiter: activate
30 per 5 minutes, for the two together every request IP address switch on
POST /connect/register (dynamic client registration)
Limiter: dcr
10 per minute every request IP address switch on
POST /auth/token with grant_type=password
Limiter: pwd_account_net
10 per 15 minutes failed sign-ins IP address and username, shared with POST /auth/pwd switch on
POST /auth/token with grant_type=password
Limiter: pwd_account
100 per 60 minutes failed sign-ins username, shared with POST /auth/pwd always
POST /auth/token with grant_type=password
Limiter: ropc_ip
30 per minute failed sign-ins IP address switch on
POST /auth/token with grant_type=password
Limiter: ropc_ip_requests
120 per minute every request IP address switch on

A few rules make these work:

  • A limit on failures isn’t spent by a success. Signing in normally never uses it up, so many people signing in from one address, such as an office or a mobile network, never use up pwd_ip or ropc_ip. A failed sign-in is a wrong password or an email or username that names no account, and through the password grant also a disabled user or one with two-factor authentication.
  • Every sign-in from one address still has a ceiling. pwd_ip_requests and ropc_ip_requests count every request, right password or wrong, at 120 a minute, far more than people signing in from one office reach. Each password check is a deliberately slow hash, so this bounds the work one address can make the server do.
  • Shared means one budget. A wrong password counts once against the same budget whether it arrives at the sign-in form or at the password grant.
  • Emails and usernames are compared in lowercase, and IPv6 addresses are grouped by their /64, so a client can’t step through its own network to reset a limit.
  • Limits on failures against an account or a user hold across replicas on PostgreSQL, MySQL and SQL Server, where they’re counted in the database, so a rollout doesn’t refill them either. If the database doesn’t answer the check within 5 seconds, the request gets a server error rather than going through. On SQLite, which runs one replica, they’re counted in the process.
  • The other limits are per process, on every database: pwd_ip, ropc_ip and every limit on every request. With three replicas, each one allows the full budget, and a restart resets it. For a limit that holds across your whole deployment, set one at your gateway, proxy or CDN, which also stops a flood before it reaches Goiabada.
  • A limit counted by IP address is only as good as the address. Behind a proxy, set up client IP addresses first.
  • Anyone who knows an email can block its password sign-in. 100 wrong passwords in an hour block that account’s password sign-in, at the form and through the password grant, for up to an hour, even with the right password, and about 100 more an hour keep it blocked. A password reset doesn’t clear the count, and anyone already signed in stays signed in. That’s the price of a bound on guessing that holds whatever address the server sees. If it happens, block the attacker at your proxy or CDN, which sees their address.

Each limit that trips writes a rate_limit_exceeded audit event, once per key and window on each replica.

The servers trim spaces around every value. An empty number or switch counts as unset and takes its default, but an empty text value is just empty: that’s how an empty listen host turns a listener off.

A number, switch or duration that doesn’t parse stops the server at start. It prints one line naming every bad value and exits with code 2, so a typo can’t leave a setting quietly on its default. A log level or log format other than the values listed, spelled in lowercase, stops it too, with unable to install the log handler and code 1. A switch is true or false, and a duration needs its unit, as in 30m, 1h or 90s.

Each server accepts only the flags of the variables it reads. Give the admin console a flag only the auth server reads, or the other way round, and it refuses to start.

A listen host is an address on the server’s own machine, not its public URL. It decides which of the machine’s addresses accept connections.

  • 0.0.0.0, the default, serves IPv4 and IPv6 alike. :: means the same. On a host with IPv6 switched off, it serves IPv4 alone.
  • A specific address serves only its own family. 127.0.0.1 takes no IPv6 connection, and ::1 no IPv4 one. A hostname such as localhost binds only the first address it resolves to, so prefer a literal address.
  • Write an IPv6 address bare or in brackets. ::1 and [::1] are the same address. A link-local address takes its zone, as in fe80::1%eth0.

Each server needs at least one listener. With both off, it refuses to start.

The rate limiter counts by client IP address, and the audit log records it. With TRUST_PROXY_HEADERS off, the server uses the address of whatever connected to it. Behind a proxy, that’s the proxy, so every client looks the same, and every user shares one rate limit: see Too many attempts or 429.

Turn TRUST_PROXY_HEADERS on behind a proxy. With TRUSTED_PROXIES empty, the server trusts one hop: the last address in X-Forwarded-For, or X-Real-IP when there’s none. That’s right behind a single proxy that sets or appends X-Forwarded-For. With more than one hop in front, such as a CDN and a proxy, list them in TRUSTED_PROXIES, and the server walks X-Forwarded-For from the right across them.

Once you set TRUSTED_PROXIES, it must include the proxy that connects to Goiabada: forwarded headers from any other address are ignored. An entry that’s neither an IP address nor a CIDR range stops the server at start, even with TRUST_PROXY_HEADERS off.

Each server seals its sessions with its own pair of keys: a 64-byte authentication key and a 32-byte encryption key. Generate a fresh pair for each server, and for each deployment:

Terminal window
openssl rand -hex 64 # authentication key
openssl rand -hex 32 # encryption key

Never reuse the example keys from a page or a sample file.

To rotate the keys without signing anyone out:

  1. Move each server’s current pair into its two _PREVIOUS variables.
  2. Set the current variables to freshly generated keys.
  3. Restart both servers. They seal new sessions with the new pair and still open sessions sealed with the old one. On Kubernetes, where a rollout runs old and new pods side by side, this takes two rollouts instead: see below.
  4. Once the longest session lifetime has passed, remove the _PREVIOUS variables.

A sign-in still in progress when you remove them may have to start again. Skip the _PREVIOUS variables altogether and everyone simply signs in again once. Rotate secrets has the commands for each platform.

GOIABADA_AES_ENCRYPTION_KEY encrypts the secrets the auth server stores: client secrets, the SMTP password, verification and password-reset codes, authenticator seeds, and the private keys that sign tokens. Only the auth server needs it.

Terminal window
openssl rand -hex 32

To rotate it, set GOIABADA_AES_ENCRYPTION_KEY to a new key and GOIABADA_AES_ENCRYPTION_KEY_PREVIOUS to the old one, then restart the auth server. At start, it re-encrypts everything under the new key in one transaction. Leaving the previous key set across restarts is harmless, but remove it once the rotation is done. If the data decrypts under neither key, the auth server refuses to start rather than risk damaging it. Rotate secrets has the procedure for each platform, and Back up the AES key where to keep the key.

When the auth server starts on an empty database, it creates the first administrator from GOIABADA_ADMIN_EMAIL and GOIABADA_ADMIN_PASSWORD, and a client for the admin console. How it gets that client’s secret depends on what you set:

  • GOIABADA_ADMINCONSOLE_OAUTH_CLIENT_SECRET set: it uses that secret and carries on running. Give both servers the same value. This is what the setup wizard writes.
  • Only GOIABADA_AUTHSERVER_BOOTSTRAP_ENV_OUTFILE set: it generates the secret and both servers’ session keys, writes them to that file, and exits. Copy them into your configuration and start again.
  • Neither set: it refuses to start, and the database stays empty.

On every later start, the database already holds the administrator and the client, and none of these variables does anything on the auth server. Changed one and can’t sign in? See Locked out of the admin console.

The four pool settings apply to PostgreSQL, MySQL and SQL Server. SQLite always uses one connection, though the values are still checked there. A value out of range stops the server at start, as does an idle limit above the open limit. A request that finds every connection busy waits for one.

The lifetime and idle time hand connections back over time, so they move to a new endpoint after a failover and a quiet replica doesn’t hold on to them. Every start writes one database connection pool log record with the pool it opened, and the migrate subcommand reads the same settings.

With several replicas, each opens its own pool against the one database. High availability shows how to size it against your database’s connection limit.

For a SQLite file that survives restarts, point GOIABADA_DB_DSN at a file, with a busy timeout and write-ahead logging:

Terminal window
GOIABADA_DB_DSN='file:/var/lib/goiabada/goiabada.db?_pragma=busy_timeout=5000&_pragma=journal_mode=WAL'

The busy timeout makes a request wait up to 5 seconds for a lock instead of failing at once. Write-ahead logging lets reads carry on while a write is in progress.

Both servers also read TZ, the standard variable, with no GOIABADA_ prefix and no flag. It sets the zone of the log timestamps and of the times the servers format. Use an IANA name such as Europe/Lisbon, or an absolute path to a zone file, either one optionally after a leading :.

The servers carry their own zone database, so the host needs none, and TZ works on Windows too. Unset, the host’s zone is used, and empty means UTC. A name that’s no zone, Local, or a zone file that doesn’t load stops the server at start.