ReallyOpenGD77 WebCPSd Deployment Guide
Do You Even Need HTTPS?
start.sh, Docker on your own laptop, etc.) – no. Skip this section, HTTPS buys you nothing here. Browsers treat http://localhost as a secure context, which is all WebUSB requires, so this is the common case for most users and it just works.You only need HTTPS if you’re accessing the app from a different device than the one running it – e.g. a Raspberry Pi or server on your LAN (or the public internet) that you open in a browser on another computer. Browsers only expose WebUSB in a secure context (https://, or http://localhost specifically) – a plain http://<its-ip>:4938 URL will never have WebUSB available, no matter how the server is configured. That’s a browser policy, not something this app can work around.
Every deployment path below has a ready-made answer for this, using Caddy for automatic HTTPS (no certbot, no cron jobs to manage). All three support both a real public domain with automatic Let’s Encrypt (for public hosting) and a self-signed certificate (for LAN-only access, no domain needed – just click through one “not private” warning per browser).
Quick Start (start.sh)
The fastest way to just try it, on any OS – runs in the foreground, in a terminal you keep open, no systemd/service setup:
git clone https://repo.w0chp.net/Chipster/ReallyOpenGD77-WebCPSd.git
cd ReallyOpenGD77-WebCPSd
./start.sh
Then open http://localhost:4938 in your browser. That’s it – the script handles the Python virtualenv and dependencies automatically.
Want this running persistently in the background instead (systemd, auto-start on boot)? See the one-line installer below, or Docker/Proxmox.
Manual Setup (without start.sh)
Already have the repo cloned (see Quick Start above)? Skip start.sh and do the venv/dependency steps yourself:
cd backend
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
uvicorn main:app --host 0.0.0.0 --port 4938 --reload
Docker Deployment
All Docker files live in deployments/docker/. Run compose commands from there (the build context still points back at the repo root, so this works out of the box):
cd deployments/docker
# Build and run
docker compose up -d
# View logs
docker compose logs -f
# Stop
docker compose down
The app is available on port 4938. If you’ll access it from a browser on a different machine than the one running Docker, see Do You Even Need HTTPS? above – you’ll want TLS in front of it for radio read/write to work.
There’s a ready-to-use caddy service commented out in deployments/docker/docker-compose.yml, paired with a Caddyfile in that same directory. To use it:
- Edit
deployments/docker/Caddyfile– replaceradio.example.comwith your real domain (needs a DNS A/AAAA record pointing at this host, and ports 80+443 reachable from the internet), or comment out that block and uncomment the:443 { tls internal ... }block instead for a self-signed/LAN-only certificate (no domain needed). - Uncomment the
caddyservice (and thevolumes:block at the bottom) indocker-compose.yml. docker compose up -das usual.
Caddy handles getting and renewing the Let’s Encrypt certificate automatically.
Raspberry Pi / Debian / Ubuntu (Recommended)
One-Line Installer
Installs system + Python dependencies, clones the app, sets it up as a systemd service on port 4938 that starts on boot, and installs Caddy as an HTTPS reverse proxy (self-signed cert by default) – all in one command:
curl -fsSL https://webcps.app/install | sudo bash
Safe to re-run any time – it updates the app in place, so this same command doubles as the updater. A dedicated sudo ogd77-webcpsd-update command is installed too, for convenience.
Public hosting instead of LAN-only? Set a real domain (with a DNS A/AAAA record pointing at this host, and ports 80+443 reachable from the internet) and Caddy gets you a proper Let’s Encrypt certificate automatically instead of the self-signed one:
curl -fsSL https://webcps.app/install | sudo DOMAIN=radio.example.com EMAIL=you@example.com bash
(EMAIL is optional – just used for certificate expiry notices. Note that sudo has to come before the env vars here, not after – sudo resets the environment by default, so DOMAIN=... sudo bash silently drops them.) Don’t want the HTTPS reverse proxy at all (fronting this with your own TLS elsewhere)? curl -fsSL https://webcps.app/install | sudo ENABLE_HTTPS=0 bash. See curl -fsSL https://webcps.app/install | bash -s -- --help for every other override (install directory, port, service user, git branch).
Prefer to do it by hand?
Already running it via Quick Start above and just want it to persist as a systemd service instead of a foreground terminal? Add a unit file yourself instead of using the one-liner:
# /etc/systemd/system/reallyopengd77-webcpsd.service
[Unit]
Description=ReallyOpenGD77 WebCPSd
After=network.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/ReallyOpenGD77-WebCPSd
ExecStart=/home/pi/ReallyOpenGD77-WebCPSd/backend/.venv/bin/uvicorn main:app --host 0.0.0.0 --port 4938
Restart=always
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now reallyopengd77-webcpsd
HTTPS for remote access
If you’ll open this from a browser on a different device than the Pi/server itself, see Do You Even Need HTTPS? above – radio read/write over WebUSB needs a secure context. One command sets up Caddy as a reverse proxy in front of the app, with either a real Let’s Encrypt certificate (public hosting) or a self-signed one (LAN-only). Standalone, no repo clone needed:
# Public hosting -- replace with your real domain (needs a DNS A/AAAA
# record pointing here, and ports 80+443 reachable from the internet):
curl -fsSL https://webcps.app/caddy-https | sudo bash -s -- radio.example.com you@example.com
# LAN-only, no domain -- self-signed cert, click through the browser
# warning once per device:
curl -fsSL https://webcps.app/caddy-https | sudo bash
Already have the repo cloned locally? sudo ./deployments/setup-caddy-https.sh [domain] [email] does the same thing. Safe to re-run any time, e.g. to switch from self-signed to a real domain once you have one.
start.sh) path.Proxmox VE (LXC)
Automated Wizard Installer
If you run Proxmox, deployments/proxmox/deploy-lxc.sh spins up a Debian 13 (Trixie) unprivileged LXC with the app running as a systemd service on port 4938, autostarting with the host – in the style of the Proxmox VE Community Scripts.
Paste this into the Proxmox host’s own shell (the node’s console/shell in the web UI, or SSH’d into the host – not a container):
curl -fsSL https://webcps.app/proxmox | bash
It’s a whiptail dialog wizard, same as the Community Scripts: pick Default Settings (recommended, minimal prompts) or Advanced Settings to choose everything yourself – hostname, cores/RAM/disk, network, and storage. Storage (both root filesystem and template storage) is a real selectable list built from your actual pvesm status, showing type and free space, not a typed-in name. The Container ID is always auto-picked, never prompted for, and the container isn’t tagged. All the apt/pip/git noise from the actual install is hidden behind a spinner – if something fails, the relevant output is shown instead of a wall of scrollback. It then downloads the Debian 13 template if it isn’t cached yet, creates and starts the container, and installs the app. When it’s done it prints the container’s URL.
Since an LXC is inherently accessed from other devices on the network (see Do You Even Need HTTPS? above), the script installs Caddy as a reverse proxy on port 443 by default, so https://<container-ip>/ works out of the box from any browser on the network – just click through the “not private” warning once per device (it’s a self-signed cert unless you set a domain, see below). Disable this entirely with ENABLE_HTTPS=0 (env var) or in the Advanced Settings dialog if you’re fronting the app with your own TLS elsewhere.
Hosting this publicly? Set a real domain (with a DNS A/AAAA record pointing at this host, and ports 80+443 reachable from the internet) and Caddy gets you a proper Let’s Encrypt certificate automatically instead of the self-signed one:
curl -fsSL https://webcps.app/proxmox | DOMAIN=radio.example.com EMAIL=you@example.com bash
(EMAIL is optional – just used for certificate expiry notices.) The Advanced Settings dialog asks for these interactively too.
Updating
The container has an update command built in. The easiest way: pct enter <CTID> and run:
update
This is also printed as a login reminder every time you enter the container. Alternatively, run it from the Proxmox host without entering the container:
curl -fsSL https://webcps.app/proxmox | bash -s -- update <CTID>
or, if you already have the repo cloned locally on the host:
./deployments/proxmox/deploy-lxc.sh update <CTID>
Every install option can also be set non-interactively via environment variables (CTID, CT_HOSTNAME, CORES, RAM_MB, DISK_GB, BRIDGE, IP_CONFIG, STORAGE, ENABLE_HTTPS, DOMAIN, EMAIL, GIT_REF, YES=1 to skip all prompts) – see the comment header in deploy-lxc.sh for the full list.
FreeBSD
This guide explains how to install, configure, and run ReallyOpenGD77 WebCPSd as a system service on FreeBSD using the provided rc.d script (deployments/freebsd/rogd77-webcpsd).
1. Install Prerequisites
sudo pkg install git python311 rust
git– to clone the repository.python311– adjust to whichever Python 3.9+ you prefer; the version just needs to match what you use for the venv in step 3.rust– required, not optional: this app’spydanticdependency compiles a Rust extension (pydantic-core) duringpip install, and PyPI does not publish prebuilt wheels for FreeBSD (pydantic/pydantic-core#773). Without a Rust toolchain, thepip installin step 3 fails outright. The Rust build itself is slow (several minutes on modest hardware) – this is normal, not a hang.
2. Clone the Repository
Clone directly into the location the service will run from – the default the rc.d script expects is /usr/local/share/rogd77-webcpsd:
sudo git clone https://repo.w0chp.net/Chipster/ReallyOpenGD77-WebCPSd.git /usr/local/share/rogd77-webcpsd
sudo chown -R nobody:nobody /usr/local/share/rogd77-webcpsd
(Using a different path is fine – just set rogd77_webcpsd_dir accordingly in step 5.)
3. Set Up the Python Virtual Environment
Build it as the nobody user (or whichever user you plan to run the service as) so file ownership is correct from the start. --no-cache-dir avoids pip trying to write a cache under nobody’s (nonexistent) home directory:
sudo -u nobody python3.11 -m venv /usr/local/share/rogd77-webcpsd/backend/.venv
sudo -u nobody /usr/local/share/rogd77-webcpsd/backend/.venv/bin/pip install --no-cache-dir --upgrade pip
sudo -u nobody /usr/local/share/rogd77-webcpsd/backend/.venv/bin/pip install --no-cache-dir -r /usr/local/share/rogd77-webcpsd/backend/requirements.txt
4. Install the Service Script
sudo cp /usr/local/share/rogd77-webcpsd/deployments/freebsd/rogd77-webcpsd /usr/local/etc/rc.d/rogd77_webcpsd
sudo chmod +x /usr/local/etc/rc.d/rogd77_webcpsd
(The file in the repo is named rogd77-webcpsd (hyphen, matching the project name); installed into rc.d it’s renamed to rogd77_webcpsd (underscore) – see the note on underscores vs. hyphens below.)
5. Configure the Service in rc.conf
rc.conf is sourced as shell code, and POSIX shell variable names can’t contain -. All of this service’s settings use rogd77_webcpsd_* (underscore) even though the project name itself has a hyphen.# Enable the service
sudo sysrc rogd77_webcpsd_enable="YES"
# (Optional) Change the install directory (defaults to /usr/local/share/rogd77-webcpsd)
sudo sysrc rogd77_webcpsd_dir="/usr/local/share/rogd77-webcpsd"
# (Optional) Change the host/ip to bind to (defaults to 0.0.0.0)
sudo sysrc rogd77_webcpsd_host="0.0.0.0"
# (Optional) Change the port (defaults to 4938)
sudo sysrc rogd77_webcpsd_port="4938"
# (Optional) Change the user (defaults to nobody)
sudo sysrc rogd77_webcpsd_user="nobody"
6. Service Management
# Start the service
sudo service rogd77_webcpsd start
# Stop the service
sudo service rogd77_webcpsd stop
# Restart the service
sudo service rogd77_webcpsd restart
# Check the service status
sudo service rogd77_webcpsd status
If start reports an error about the venv or uvicorn not being found, re-check step 3 – it means the virtual environment wasn’t created (or wasn’t created at the path rogd77_webcpsd_dir points to).
7. Logs
Stdout and stderr from the application are captured to:
/var/log/rogd77_webcpsd/rogd77_webcpsd.log
tail -f /var/log/rogd77_webcpsd/rogd77_webcpsd.log
8. Updating
To pull a new version later:
cd /usr/local/share/rogd77-webcpsd
sudo -u nobody git pull
sudo -u nobody /usr/local/share/rogd77-webcpsd/backend/.venv/bin/pip install --no-cache-dir -r backend/requirements.txt
sudo service rogd77_webcpsd restart
HTTPS for remote access
For HTTPS/remote access, the same Do You Even Need HTTPS? guidance above applies. deployments/setup-caddy-https.sh is Debian/Ubuntu-oriented, so it doesn’t run on FreeBSD – instead, front the app with pkg install caddy and a Caddyfile reverse-proxying to 127.0.0.1:4938.
See Also
- Linux WebUSB Setup – required one-time step on Linux clients before radio read/write will work.
- Supported Radios
- Contributing Guide