Frame docs · Reference
Configuration reference
Settings come from environment variables, optionally loaded from a KEY=value file. With no configuration at all, frame-server runs a working server on plain HTTP port 8080 with its data in ./frame-data.
The config file
| Install | File | Restart after a change |
|---|---|---|
| Linux installer | /etc/frame/frame.conf (root-only, mode 0600) | sudo systemctl restart frame |
| Docker Compose | deploy/frame.conf, next to docker-compose.yml | docker compose up -d |
| Binary | any file passed with --config | restart the process |
- Linux installerFile:
/etc/frame/frame.conf(root-only, mode 0600)Restart after a change:sudo systemctl restart frame - Docker ComposeFile:
deploy/frame.conf, next todocker-compose.ymlRestart after a change:docker compose up -d - BinaryFile:any file passed with
--configRestart after a change:restart the process
- One
KEY=valueper line; lines starting with#are comments. Real environment variables override the file. - Wrap values containing spaces, quotes or
#in double quotes (FRAME_NAME="Daniel's Frame"). Wrap secrets containing$or\in single quotes. - The server name, who can join, members' invites and the limits can also be changed in the admin panel, unless they are set in the file: anything set in the file shows as locked ("set in frame.conf") in the panel. Leave them out to manage them from the panel.
- Keep the file private and never commit it: it can contain passwords and keys. The annotated example is
deploy/frame.conf.example.
In Docker, the compose files fix FRAME_DATA_DIR and FRAME_LISTEN inside the container, and the Caddy file also clears FRAME_DOMAIN and sets FRAME_PUBLIC_URL and FRAME_TRUSTED_PROXIES, overriding frame.conf.
Settings
Basics and sign-in
| Setting | Default | What it does |
|---|---|---|
FRAME_PUBLIC_URL | https://$FRAME_DOMAIN | The address(es) people type into the app, comma-separated if the server is reachable under several names (the first is used in invite links). Required for Sign in with Apple and Google: without it, those methods are turned off. |
FRAME_NAME | Frame | Server name shown in the app. Panel setting. |
FRAME_REGISTRATION | invite | invite (new accounts need an invite), open (anyone who knows the address) or closed (only admins create accounts). Panel setting. |
FRAME_USER_INVITES | true | On invite-only servers, whether members' invite links from the app can also bring in new people. Panel setting. |
FRAME_AUTH_METHODS | email,phone,apple,google | Sign-in methods offered. Phone numbers are just identifiers: Frame sends no SMS. |
FRAME_APPLE_AUDIENCES | com.crowtech.frame,com.crowtech.frame.signin | Accepted audiences for Sign in with Apple. Change only if you build and sign the apps yourself. |
FRAME_GOOGLE_CLIENT_IDS | the official Frame apps' client ids | Accepted audiences for Google sign-in. Change only if you build the apps yourself. |
FRAME_PUBLIC_URLDefault:https://$FRAME_DOMAINWhat it does:The address(es) people type into the app, comma-separated if the server is reachable under several names (the first is used in invite links). Required for Sign in with Apple and Google: without it, those methods are turned off.FRAME_NAMEDefault:FrameWhat it does:Server name shown in the app. Panel setting.FRAME_REGISTRATIONDefault:inviteWhat it does:invite(new accounts need an invite),open(anyone who knows the address) orclosed(only admins create accounts). Panel setting.FRAME_USER_INVITESDefault:trueWhat it does:On invite-only servers, whether members' invite links from the app can also bring in new people. Panel setting.FRAME_AUTH_METHODSDefault:email,phone,apple,googleWhat it does:Sign-in methods offered. Phone numbers are just identifiers: Frame sends no SMS.FRAME_APPLE_AUDIENCESDefault:com.crowtech.frame,com.crowtech.frame.signinWhat it does:Accepted audiences for Sign in with Apple. Change only if you build and sign the apps yourself.FRAME_GOOGLE_CLIENT_IDSDefault:the official Frame apps' client idsWhat it does:Accepted audiences for Google sign-in. Change only if you build the apps yourself.
Network and HTTPS
| Setting | Default | What it does |
|---|---|---|
FRAME_DOMAIN | — | Serve HTTPS on :443 with automatic Let's Encrypt certificates and redirect :80 to HTTPS. With the Linux installer, run sudo sh install.sh --reconfigure after setting it. |
FRAME_ACME_EMAIL | — | Optional contact e-mail for Let's Encrypt. |
FRAME_LISTEN | :8080 | Plain-HTTP listen address, for use behind a reverse proxy or tunnel. 127.0.0.1:8080 keeps it reachable from this machine only. Ignored when FRAME_DOMAIN is set. |
FRAME_TRUSTED_PROXIES | — | Comma-separated IPs or CIDRs of reverse proxies whose X-Forwarded-For is trusted, so rate limits see real visitors. The word cloudflare also trusts CF-Connecting-IP, but only from loopback and the listed CIDRs. Leave unset when phones connect directly. |
FRAME_DOMAINDefault:—What it does:Serve HTTPS on:443with automatic Let's Encrypt certificates and redirect:80to HTTPS. With the Linux installer, runsudo sh install.sh --reconfigureafter setting it.FRAME_ACME_EMAILDefault:—What it does:Optional contact e-mail for Let's Encrypt.FRAME_LISTENDefault::8080What it does:Plain-HTTP listen address, for use behind a reverse proxy or tunnel.127.0.0.1:8080keeps it reachable from this machine only. Ignored whenFRAME_DOMAINis set.FRAME_TRUSTED_PROXIESDefault:—What it does:Comma-separated IPs or CIDRs of reverse proxies whoseX-Forwarded-Foris trusted, so rate limits see real visitors. The wordcloudflarealso trustsCF-Connecting-IP, but only from loopback and the listed CIDRs. Leave unset when phones connect directly.
Admin panel
| Setting | Default | What it does |
|---|---|---|
FRAME_ADMIN | on | Web admin panel at /admin: on, local (only from loopback and FRAME_TRUSTED_PROXIES networks) or off. |
FRAME_ADMIN_LISTEN | — | A separate listen address for the panel, such as 127.0.0.1:8081; /admin is then not served on the main address. |
FRAME_ADMIN_APP | auto | Server admin in the apps: auto (only when the panel is on the main address), on (also with a private panel; FRAME_ADMIN=off still wins) or off. |
FRAME_ADMINDefault:onWhat it does:Web admin panel at/admin:on,local(only from loopback andFRAME_TRUSTED_PROXIESnetworks) oroff.FRAME_ADMIN_LISTENDefault:—What it does:A separate listen address for the panel, such as127.0.0.1:8081;/adminis then not served on the main address.FRAME_ADMIN_APPDefault:autoWhat it does:Server admin in the apps:auto(only when the panel is on the main address),on(also with a private panel;FRAME_ADMIN=offstill wins) oroff.
See Keep the admin panel private.
Storage
| Setting | Default | What it does |
|---|---|---|
FRAME_DATA_DIR | ./frame-data | The database (frame.db), local media (media/) and generated secrets (secrets.json). The installer and Docker set it (/var/lib/frame, /data). Back it up. |
FRAME_STORAGE | local | local or s3. |
FRAME_S3_ENDPOINT | — | For example https://<account-id>.r2.cloudflarestorage.com. |
FRAME_S3_BUCKET | — | Bucket name (a private bucket). |
FRAME_S3_REGION | auto | auto for R2. |
FRAME_S3_ACCESS_KEY_ID, FRAME_S3_SECRET_ACCESS_KEY | — | Credentials of a token scoped to the bucket. |
FRAME_S3_PREFIX | frame/ | Object key prefix. |
FRAME_S3_ENCRYPT | true | Encrypt objects (AES-256-GCM) before upload. The key is in secrets.json: losing it loses the media. |
FRAME_DATA_DIRDefault:./frame-dataWhat it does:The database (frame.db), local media (media/) and generated secrets (secrets.json). The installer and Docker set it (/var/lib/frame,/data). Back it up.FRAME_STORAGEDefault:localWhat it does:localors3.FRAME_S3_ENDPOINTDefault:—What it does:For examplehttps://<account-id>.r2.cloudflarestorage.com.FRAME_S3_BUCKETDefault:—What it does:Bucket name (a private bucket).FRAME_S3_REGIONDefault:autoWhat it does:autofor R2.FRAME_S3_ACCESS_KEY_ID,FRAME_S3_SECRET_ACCESS_KEYDefault:—What it does:Credentials of a token scoped to the bucket.FRAME_S3_PREFIXDefault:frame/What it does:Object key prefix.FRAME_S3_ENCRYPTDefault:trueWhat it does:Encrypt objects (AES-256-GCM) before upload. The key is insecrets.json: losing it loses the media.
See Storage & sizing.
Push and invites
| Setting | Default | What it does |
|---|---|---|
FRAME_PUSH_RELAY | https://frame.crow.sg | Push relay base URL. An empty value turns push off. |
FRAME_INVITE_BASE | https://frame.crow.sg/i | Base of shareable invite links. The server address travels in the link's # fragment, which browsers never send to that page. |
FRAME_PUSH_RELAYDefault:https://frame.crow.sgWhat it does:Push relay base URL. An empty value turns push off.FRAME_INVITE_BASEDefault:https://frame.crow.sg/iWhat it does:Base of shareable invite links. The server address travels in the link's#fragment, which browsers never send to that page.
Limits
| Setting | Default | What it does |
|---|---|---|
FRAME_MAX_UPLOAD_MB | 50 | Maximum upload size per photo or video. Panel setting. |
FRAME_MAX_VIDEO_SECONDS | 15 | Maximum video length the apps record and accept. Panel setting. |
FRAME_MAX_FRIENDS | 0 | Maximum friends per user; 0 is unlimited. Panel setting. |
FRAME_MAX_UPLOAD_MBDefault:50What it does:Maximum upload size per photo or video. Panel setting.FRAME_MAX_VIDEO_SECONDSDefault:15What it does:Maximum video length the apps record and accept. Panel setting.FRAME_MAX_FRIENDSDefault:0What it does:Maximum friends per user;0is unlimited. Panel setting.
E-mail and logging
| Setting | Default | What it does |
|---|---|---|
FRAME_SMTP_URL | — | For example smtps://user:[email protected]:465. Enables "forgot password" e-mails; without it, admins reset passwords in the panel. |
FRAME_SMTP_FROM | — | From address for reset e-mails. |
FRAME_LOG_LEVEL | info | debug, info, warn or error. Logs are JSON on stderr and never contain passwords, tokens or message text. |
FRAME_SMTP_URLDefault:—What it does:For examplesmtps://user:[email protected]:465. Enables "forgot password" e-mails; without it, admins reset passwords in the panel.FRAME_SMTP_FROMDefault:—What it does:From address for reset e-mails.FRAME_LOG_LEVELDefault:infoWhat it does:debug,info,warnorerror. Logs are JSON on stderr and never contain passwords, tokens or message text.
For e-mail password resets, for example:
FRAME_SMTP_URL='smtps://user:[email protected]:465'[email protected]Commands
With the Linux installer, run admin commands as sudo frame-admin … (for example sudo frame-admin user list; frame-admin runs frame-server admin … as the service's user). With Docker, use docker compose exec frame frame-server ….
frame-server [serve] [--config FILE] run the server (default command)frame-server dev dev server on :8787, ./server/.devdata, seeded demo usersframe-server admin setup-link print a one-time link to create the first admin (also printed at first start)frame-server admin user listframe-server admin user create --email E | --phone P [--name N] [--username U] [--admin] (prints a one-time password)frame-server admin user promote <username> make an existing user an admin of the web panelframe-server admin user demote <username> (refuses to remove the last admin)frame-server admin user reset-2fa <username> turn off an admin's two-factor authenticationframe-server admin user reset-password <username|email|phone> (prints a one-time password)frame-server admin user unlink <username|email|phone> apple|google (removes a linked sign-in, signs out all devices)frame-server admin user delete <username>frame-server admin invite create <username> (prints that user's friend-invite link)frame-server admin invite server [--uses N] [--days D] [--note TEXT] (prints a server invite link; --days 0 = never expires)frame-server backup <file.db> online, consistent SQLite backupframe-server versionDev mode differs on purpose: it opens registration, turns push off and seeds alice (password password123) as an admin instead of printing a setup link. See Try it locally.
For AI tools: this page as Markdown, the page index and all pages in one file.