Systemd and SQLite
The lightest option: one binary, an SQLite file and a systemd service. No Docker. Run the commands below as root.
mkdir -p /opt/gowave/frontend/dist && cd /opt/gowaveARCH=$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')curl -fL -o gowave-server https://github.com/aaaSaZaN/gowave/releases/latest/download/gowave-linux-$ARCHcurl -fLO https://github.com/aaaSaZaN/gowave/releases/latest/download/gowave-frontend.tar.gzchmod +x gowave-server && tar -xzf gowave-frontend.tar.gz -C frontend/distChecksums are in SHA256SUMS next to the release files.
Settings
Section titled “Settings”cat > .env <<ENVAPP_HOST=127.0.0.1APP_PORT=3000APP_SECRET=$(openssl rand -hex 32)DB_DRIVER=sqliteDATABASE_URL=gowave.dbPANEL_DOMAIN=panel.yourdomain.comSUB_PUBLIC_DOMAIN=panel.yourdomain.com/api/subENVchmod 600 .envAll variables are listed in the reference.
Service
Section titled “Service”The panel does not run as root: the service runs as the gowave system user. Create /etc/systemd/system/gowave.service:
[Unit]Description=GoWave PanelAfter=network-online.targetWants=network-online.target
[Service]Type=simpleUser=gowaveGroup=gowaveWorkingDirectory=/opt/gowaveExecStart=/opt/gowave/gowave-serverRestart=alwaysRestartSec=5LimitNOFILE=65535
[Install]WantedBy=multi-user.targetThe binary creates the user and the service restrictions itself:
/opt/gowave/gowave-server service install /opt/gowaveln -sfn /opt/gowave/gowave-server /usr/local/bin/gowavesystemctl enable --now gowaveservice install:
- creates the
gowavesystem group and user, with no shell and no home directory, unless they exist; - gives
/opt/gowaveto that user, with mode0750on the directory and0600on.env; - writes
/etc/systemd/system/gowave.service.d/90-unprivileged.confand runssystemctl daemon-reload.
It never touches gowave.service itself, so your Environment= lines and other settings there stay. The directory has to be a dedicated one: the command refuses /opt, /usr/local, anything under /home and other shared paths. Only Latin letters, digits and _ . / - are allowed in the path.
The second line creates the global gowave command. The installer does this for you, and the service, running as gowave, cannot write to /usr/local/bin.
Service restrictions
Section titled “Service restrictions”90-unprivileged.conf overrides the user and the start command and walls the service off from the rest of the system:
[Service]User=gowaveGroup=gowaveWorkingDirectory=/opt/gowaveExecStart=ExecStart=/opt/gowave/gowave-serverDynamicUser=falseProtectSystem=strictProtectHome=truePrivateDevices=truePrivateTmp=trueNoNewPrivileges=trueMemoryDenyWriteExecute=trueProtectKernelTunables=trueProtectKernelModules=trueProtectControlGroups=trueRestrictSUIDSGID=trueLockPersonality=trueUMask=0077ReadWritePaths=/opt/gowaveRuntimeDirectory=gowaveRuntimeDirectoryMode=0700RuntimeDirectoryPreserve=yesRestart=alwaysCapabilityBoundingSet=CapabilityBoundingSet=CAP_NET_BIND_SERVICEAmbientCapabilities=AmbientCapabilities=CAP_NET_BIND_SERVICEThe service can write only to /opt/gowave, /run/gowave and its own private /tmp. The rest of the file system is read-only for it, and /home and /root are not visible at all. The one privilege left is CAP_NET_BIND_SERVICE, so APP_PORT can be below 1024 if you set it that way.
DynamicUser is off on purpose: the SQLite files must belong to a stable user.
The panel code is owned by root. The service account cannot change gowave-server or frontend/, so a compromised panel cannot plant a binary that root later runs (the installer, gowave rescue via sudo). /opt/gowave is root:gowave with the sticky bit: the panel creates its database files there but cannot replace or remove root’s files. .env is read-only for the panel.
Updates from the panel run in a separate root service. The panel leaves a request in /run/gowave, gowave-update.path starts gowave-update.service, which downloads the release, checks it against SHA256SUMS, installs it and restarts the panel.
If the panel needs to write somewhere else, say a separate backup directory, add an override of your own. 90-unprivileged.conf is rewritten by every service install and every update through the installer, so do not edit it:
systemctl edit gowave[Service]ReadWritePaths=/srv/gowave-backupsGive the directory to the service user as well: chown gowave:gowave /srv/gowave-backups.
CLI commands
Section titled “CLI commands”Run them as gowave from the panel directory. If you run them as root, SQLite may create database side files (gowave.db-wal, gowave.db-shm) that the service then cannot open:
cd /opt/gowave && sudo -u gowave gowave rescueAll commands are in the CLI reference.
Old root service
Section titled “Old root service”If the panel was installed with User=root (versions before v1.1.11 did that), there is nothing to do. On its first start under systemd the new binary runs service install for its working directory and exits, and systemd restarts it as gowave. The installer also reapplies the restrictions on every update.
Next, set up a reverse proxy.