Configuration
Guestroll is configured with environment variables only. Every variable is optional: an empty instance starts with safe defaults. A complete template is in .env.example.
Invalid values stop the server at startup with a message naming the variable, instead of running with a silent fallback.
Docker and network
These are read by the container or by docker-compose.yml, not by the app itself.
| Variable | Default | Description |
|---|---|---|
PORT | 3000 | Port the server listens on inside the container (or on the machine without Docker). The health check follows it. |
HOSTNAME | 0.0.0.0 | Interface the server binds to inside the container. |
HOST_PORT | 3000 | Compose only: port published on the host. |
HOST_BIND | 0.0.0.0 | Compose only: host interface to publish on. 127.0.0.1 keeps Guestroll reachable only through a local reverse proxy. |
DATA_PATH | guestroll-data | Compose only: named volume or host path mounted on /data. |
PUID / PGID | 1001 | User and group that own the data folder and run the server. Match your host user for bind mounts. |
TZ | UTC | Time zone of the stats (hourly and daily buckets), e.g. Europe/Paris. |
GUESTROLL_IMAGE | ghcr.io/achedon12/guestroll:latest | Compose only: image to run. Pin a version in production. |
CONTAINER_NAME | guestroll | Compose only: lets you run several instances side by side. |
Instance
| Variable | Default | Description |
|---|---|---|
APP_NAME | Guestroll | Shown in page titles, the admin header and social cards. |
PUBLIC_URL | (request host) | Public origin encoded in QR codes and social cards, e.g. https://photos.example.com. Set it: behind a proxy, the request host may be an internal name. |
DATA_DIR | ./data (/data in Docker) | Where the SQLite database and photos live. |
Languages
Guestroll speaks English (en), French (fr), Italian (it) and Spanish (es). Each visitor gets, in this order:
- the language they picked with the language menu (kept in their browser);
- the album's language, if the hosts set one in the event settings;
- their browser's language, if supported and
DETECT_BROWSER_LOCALEis on; DEFAULT_LOCALE.
| Variable | Default | Description |
|---|---|---|
DEFAULT_LOCALE | en | Language used when nothing more specific applies. |
DETECT_BROWSER_LOCALE | true | false skips step 3: everyone without a choice gets the album's language or DEFAULT_LOCALE. |
Security
| Variable | Default | Description |
|---|---|---|
ADMIN_USERNAME | admin | Username of the owner account created on first start. |
ADMIN_PASSWORD | (generated) | Password of that owner account. When set, it wins on every start (and the account stays an owner): it is the way back in if every other password is lost. When empty, a password is generated on first start and printed once in the logs; change it in Admin → Settings. Other accounts are created from the admin. |
SESSION_SECRET | (generated) | Key that signs and encrypts cookies, 32+ characters. When empty, one is generated and stored in DATA_DIR/.session-secret. Changing it signs everyone out. |
TRUST_PROXY | false | Read the client IP from X-Forwarded-For and the host from X-Forwarded-Host. Turn it on only behind a reverse proxy you control, or rate limits can be dodged with a forged header. |
SECURE_COOKIES | true in production | Cookies only travel over HTTPS. Set to false only to test over plain HTTP on a local network. |
Uploads
| Variable | Default | Description |
|---|---|---|
MAX_UPLOAD_MB | 25 | Largest accepted photo. Phones resize before sending, so most uploads weigh 1–4 MB. Raise your reverse proxy's body limit above this. |
MAX_IMAGE_MEGAPIXELS | 80 | Images above this are refused before decoding, which protects memory from "decompression bombs". |
DEFAULT_CAPTURE_MODE | camera | Capture mode preselected for new events: camera, picker or both. |
Rate limits
Per minute. Upload limits apply per guest, login limits per client IP.
| Variable | Default | Description |
|---|---|---|
RATE_LIMIT_UPLOADS_PER_MINUTE | 30 | Photos one guest can send per minute. |
RATE_LIMIT_LOGIN_PER_MINUTE | 5 | Admin login attempts per IP. |
RATE_LIMIT_UNLOCK_PER_MINUTE | 10 | Access-code attempts per guest; three times this value is the ceiling for wrong codes on one event, across all guests. |
Storage
Photos, logos and voice messages go to a folder under DATA_DIR by default. Set STORAGE_DRIVER=s3 to keep them in any S3-compatible bucket instead (AWS S3, Backblaze B2, Cloudflare R2, Wasabi, Garage, RustFS, SeaweedFS…). The SQLite database always stays in DATA_DIR, so keep the volume either way.
| Variable | Default | Description |
|---|---|---|
STORAGE_DRIVER | local | local or s3. |
S3_ENDPOINT | (none) | e.g. https://s3.eu-west-3.amazonaws.com, https://<account>.r2.cloudflarestorage.com, http://garage:3900. |
S3_REGION | us-east-1 | Region used to sign requests. Most self-hosted servers accept the default. |
S3_BUCKET | (none) | The bucket must exist. Keep it private: Guestroll serves every file itself, after checking access. |
S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY | (none) | Credentials allowed to read, write, list and delete in the bucket. |
S3_PREFIX | (none) | Folder inside the bucket, to share one bucket between instances. |
S3_FORCE_PATH_STYLE | true | true sends endpoint/bucket/key; false sends bucket.endpoint/key (AWS virtual-hosted style). |
At startup Guestroll writes, reads back and deletes a small probe file, and logs [storage] S3 bucket NOT usable with the reason if that fails. Files are never served from the bucket directly, so photos stay private even with a bucket on a public endpoint. To move existing photos to a bucket, make a backup (Admin → Settings → Backup), switch the driver, then restore it.
Email
Optional. Without SMTP, everything else works and no email is ever sent. SMTP can also be set in Admin → Settings → Email, where the password is stored encrypted (AES-256-GCM, key derived from SESSION_SECRET). When SMTP_HOST is set, the environment wins and the admin form is read-only.
| Variable | Default | Description |
|---|---|---|
SMTP_HOST | (none) | SMTP server, e.g. smtp.fastmail.com. Setting it turns email on. |
SMTP_PORT | 587 | 587 (STARTTLS) or 465 (TLS). |
SMTP_SECURE | false | true for implicit TLS on port 465. |
SMTP_USER / SMTP_PASSWORD | (none) | Credentials, when the server asks for them. |
SMTP_FROM | SMTP_USER | Sender, e.g. Guestroll <photos@example.com>. |
ADMIN_EMAIL | (none) | Where alerts and event summaries go. Settable in the admin when empty. |
SCHEDULER_INTERVAL_SECONDS | 60 | How often the background worker sends queued mail, closes scheduled events and checks alerts. 0 turns it off. |
Emails wait in a queue in the database and are retried after 1, 5, 15 minutes, 1 and 6 hours, then marked failed (Admin → Settings shows them, with a retry button). Set PUBLIC_URL: links in emails are built from it, since a background job has no request to read the host from.