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:

Terminal
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.sh

It does this, and nothing else:

  1. picks the release binary for this CPU (amd64, arm64 or armv7);
  2. downloads it plus SHA256SUMS from the GitHub release and verifies the SHA-256 checksum before installing anything;
  3. installs /usr/local/bin/frame-server, the /usr/local/bin/frame-admin helper and /etc/systemd/system/frame.service;
  4. creates /etc/frame/frame.conf (mode 0600) from the example on the first run; an existing config is never touched;
  5. lets the service bind ports 80 and 443 only if FRAME_DOMAIN is set;
  6. 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.

  1. Step 1: Point your domain at the server and open the firewall

    Add an A record for frame with the server's IP (DNS only on Cloudflare). Then allow SSH and web traffic, and nothing else:

    Terminal
    sudo apt install -y ufw          # Debian, Ubuntu, Raspberry Pi OSsudo ufw allow OpenSSHsudo ufw allow 80,443/tcpsudo ufw enable
  2. Step 2: Install, then configure

    Run the installer as above, then sudo nano /etc/frame/frame.conf, remove the # and fill in:

    /etc/frame/frame.conf
    FRAME_DOMAIN=frame.example.comFRAME_PUBLIC_URL=https://frame.example.com[email protected]FRAME_NAME="Our Frame"

    Leave FRAME_TRUSTED_PROXIES unset: phones connect to Frame directly, with no proxy in between.

  3. Step 3: Start it

    Terminal
    sudo 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 watching

    Open https://frame.example.com/healthz. It should say ok. 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.

  1. 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.conf
    FRAME_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:8080 means only programs on this machine, such as the tunnel, can reach the server. FRAME_TRUSTED_PROXIES=cloudflare,127.0.0.1/32 makes Frame trust the visitor IP that Cloudflare passes along (the CF-Connecting-IP header), but only from cloudflared on this machine: rate limits then apply per visitor, and nobody else can fake the header. Don't set FRAME_DOMAIN here.

  2. Step 2: Start it

    Terminal
    sudo systemctl start framecurl http://127.0.0.1:8080/healthz   # → ok
  3. Step 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 installs cloudflared from Cloudflare's package repository, and one is sudo cloudflared service install <token>. Run both on your machine; the token is a secret. Under Public Hostname, set subdomain frame, your domain, service type HTTP and URL localhost:8080, and save.

  4. Step 4: Test it

    From your phone, on mobile data rather than Wi-Fi, open https://frame.example.com/healthz. It should show ok. 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:

/etc/frame/frame.conf
FRAME_LISTEN=127.0.0.1:8080FRAME_PUBLIC_URL=https://frame.example.comFRAME_TRUSTED_PROXIES=127.0.0.1/32,::1/128

FRAME_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:

/etc/caddy/Caddyfile
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 Host and X-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; and proxy_read_timeout 1h;).
  • Add the proxy's IP to FRAME_TRUSTED_PROXIES.

Installer options

  • --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.

The environment variable FRAME_REPO (default Crow-Tech-Pte-Ltd/frame) points the installer at a fork.

Useful commands

Terminal
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-admin

The 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.

WhatsApp