Frame docs · Install
Install with the Linux installer (systemd)
For Debian, Ubuntu, Raspberry Pi OS, Fedora and other Linux systems with systemd, on amd64, arm64 or armv7. The installer sets up frame-server as a locked-down service; how the server is reached depends on your machine.
What the installer does
Download it, read it, then run it:
curl -fsSLO https://github.com/Crow-Tech-Pte-Ltd/frame/releases/latest/download/install.shless install.sh # read it (press q to quit)sudo sh install.shIt does this, and nothing else:
- picks the release binary for this CPU (amd64, arm64 or armv7);
- downloads it plus
SHA256SUMSfrom the GitHub release and verifies the SHA-256 checksum before installing anything; - installs
/usr/local/bin/frame-server, the/usr/local/bin/frame-adminhelper and/etc/systemd/system/frame.service; - creates
/etc/frame/frame.conf(mode 0600) from the example on the first run; an existing config is never touched; - lets the service bind ports 80 and 443 only if
FRAME_DOMAINis set; - enables the service, and on upgrades restarts it if it was running.
Re-running it is safe. Data in /var/lib/frame is never touched. Use sudo frame-admin … for admin commands: it runs them as the service's own user.
On a VPS with built-in HTTPS
The simplest always-on setup: Frame gets and renews its own certificate from Let's Encrypt. The quickstart walks through it with every command.
Step 1: Point your domain at the server and open the firewall
Add an
Arecord forframewith the server's IP (DNS only on Cloudflare). Then allow SSH and web traffic, and nothing else:Terminalsudo apt install -y ufw # Debian, Ubuntu, Raspberry Pi OSsudo ufw allow OpenSSHsudo ufw allow 80,443/tcpsudo ufw enableStep 2: Install, then configure
Run the installer as above, then
sudo nano /etc/frame/frame.conf, remove the#and fill in:/etc/frame/frame.confFRAME_DOMAIN=frame.example.comFRAME_PUBLIC_URL=https://frame.example.com[email protected]FRAME_NAME="Our Frame"Leave
FRAME_TRUSTED_PROXIESunset: phones connect to Frame directly, with no proxy in between.Step 3: Start it
Terminalsudo sh install.sh --reconfigure # lets Frame use ports 80/443 because FRAME_DOMAIN is setsudo systemctl start framesystemctl status frame # should say "active (running)"journalctl -u frame -f # live logs; Ctrl+C to stop watchingOpen
https://frame.example.com/healthz. It should sayok. Then create your admin account.
At home with Cloudflare Tunnel
Run Frame on a Raspberry Pi 3B+, 4, 5 or Zero 2 W, or any Linux PC, with no open ports and no router configuration. This also works behind carrier-grade NAT. You need a 64-bit OS (such as Raspberry Pi OS Lite 64-bit, Debian or Ubuntu) and a domain whose DNS is on Cloudflare: adding a domain to Cloudflare's free plan is free. Prefer an SSD or USB stick for the data over the SD card, which wears out, or keep media in R2.
Step 1: Install and configure Frame
Update the machine and run the installer as above; skip the firewall step, nothing needs to be opened. Then
sudo nano /etc/frame/frame.conf:/etc/frame/frame.confFRAME_LISTEN=127.0.0.1:8080FRAME_PUBLIC_URL=https://frame.example.comFRAME_TRUSTED_PROXIES=cloudflare,127.0.0.1/32FRAME_NAME="Our Frame"FRAME_LISTEN=127.0.0.1:8080means only programs on this machine, such as the tunnel, can reach the server.FRAME_TRUSTED_PROXIES=cloudflare,127.0.0.1/32makes Frame trust the visitor IP that Cloudflare passes along (theCF-Connecting-IPheader), but only fromcloudflaredon this machine: rate limits then apply per visitor, and nobody else can fake the header. Don't setFRAME_DOMAINhere.Step 2: Start it
Terminalsudo systemctl start framecurl http://127.0.0.1:8080/healthz # → okStep 3: Create the tunnel
In the Cloudflare dashboard, go to Zero Trust → Networks → Tunnels → Create a tunnel, choose Cloudflared and name it
frame. Choose your OS and architecture (Debian, arm64 for a Pi). Cloudflare shows two commands: one installscloudflaredfrom Cloudflare's package repository, and one issudo cloudflared service install <token>. Run both on your machine; the token is a secret. Under Public Hostname, set subdomainframe, your domain, service type HTTP and URLlocalhost:8080, and save.Step 4: Test it
From your phone, on mobile data rather than Wi-Fi, open
https://frame.example.com/healthz. It should showok. Then create your admin account.
Behind Caddy, nginx or another proxy
If the machine already serves other websites, let its web server handle HTTPS. Install Frame as above, but don't set FRAME_DOMAIN:
FRAME_LISTEN=127.0.0.1:8080FRAME_PUBLIC_URL=https://frame.example.comFRAME_TRUSTED_PROXIES=127.0.0.1/32,::1/128FRAME_TRUSTED_PROXIES tells Frame that the X-Forwarded-For header from the proxy on this machine carries the real visitor IP. For Caddy, add this to /etc/caddy/Caddyfile (the commented version is deploy/Caddyfile), then run sudo systemctl reload caddy and sudo systemctl start frame:
frame.example.com { request_body { max_size 64MB } reverse_proxy 127.0.0.1:8080 { flush_interval -1 health_uri /healthz }}Using nginx or another proxy? You need four things:
- Pass
HostandX-Forwarded-For. - Allow request bodies of about 64 MB (nginx:
client_max_body_size 64m;). - Don't buffer
/api/v1/events, which is a Server-Sent Events stream (nginx:proxy_buffering off;andproxy_read_timeout 1h;). - Add the proxy's IP to
FRAME_TRUSTED_PROXIES.
Installer options
| Option | What it does |
|---|---|
--version vX.Y.Z | Install that release instead of the latest. |
--binary PATH | Install a locally built frame-server (no download); the unit, admin helper and example config come from the script's directory, a repository checkout's deploy/. See Binary & source. |
--start | Start the service even on the first install. |
--reconfigure | Only re-check FRAME_DOMAIN, reload and restart. Run it after setting or removing FRAME_DOMAIN. |
--uninstall | Remove the binary, unit and helper; keeps the configuration in /etc/frame and the data. |
--version vX.Y.ZInstall that release instead of the latest.--binary PATHInstall a locally builtframe-server(no download); the unit, admin helper and example config come from the script's directory, a repository checkout'sdeploy/. See Binary & source.--startStart the service even on the first install.--reconfigureOnly re-checkFRAME_DOMAIN, reload and restart. Run it after setting or removingFRAME_DOMAIN.--uninstallRemove the binary, unit and helper; keeps the configuration in/etc/frameand the data.
The environment variable FRAME_REPO (default Crow-Tech-Pte-Ltd/frame) points the installer at a fork.
Useful commands
systemctl status frame # running?journalctl -u frame -n 100 # last 100 log linessudo systemctl restart frame # after editing frame.confsudo frame-admin user list # admin commands: always through frame-adminThe service runs with a throwaway system user that can write only its data directory, a read-only view of the system and no capabilities beyond binding ports 80/443 when FRAME_DOMAIN needs them. Details in Security.
For AI tools: this page as Markdown, the page index and all pages in one file.