GiVo Server for Linux & NAS
Run the DVR companion headless on any Linux machine or NAS — Synology, Unraid, a home server, a Raspberry Pi, or Docker Desktop on a Mac or PC. Same full DVR as the desktop apps: recording, scheduling, catchup, live timeshift, commercial skip, and automatic captions for every paired device. Everything runs on x86-64 (amd64) and ARM (arm64).
Contents
1. Docker Compose — Linux & NAS (recommended)
Create a folder for the server, add this docker-compose.yml,
and start it:
services:
givo-server:
image: ghcr.io/gjdigitaldev/givo-server:latest
container_name: givo-server
network_mode: host
environment:
- GIVO_NAME=GiVo DVR
# Recommended when pairing several devices — see “Pairing” below:
# - GIVO_PASSWORD=123456
# Optional post-recording passes — see “Commercial skip & auto captions”:
# - GIVO_COMMERCIAL_SKIP=true
# - GIVO_WHISPER_CAPTIONS=true
# - GIVO_WHISPER_MODEL=distil-large-v3
volumes:
- ./config:/config
- ./recordings:/recordings
restart: unless-stopped
mkdir -p config recordings
docker compose up -d
docker compose logs givo-server
The logs command prints the server's startup banner — your
pairing code is in it.
That's the whole install. Point ./recordings at your big
disk — that's where recordings are written — then jump to
Pairing.
2. Docker Desktop — Mac & Windows
Never used Docker or a terminal? Start here.
- Install Docker Desktop (free) from docker.com, then open it. Wait until it says it's running (Mac: the whale icon in the menu bar; Windows: the whale in the system tray). Every command below fails if Docker Desktop isn't running.
- Open a command window. Mac: press Cmd + Space, type Terminal, press Return. Windows: open the Start menu, type cmd, press Enter (Command Prompt).
- Use the Copy button in the corner of each command
box below, paste into the command window, and press Return. A command
that spans several lines ending in
\is still ONE command — always copy and paste the whole box, never a single line from inside it. - Success often looks like nothing. Some commands print nothing at all when they work; each box below says what you should see.
Docker Desktop doesn't support host networking the way Linux does, so use published ports (bridge).
First: is the GiVo Server desktop app also running on this same computer? If yes, skip the block below and use the alternate-port command further down — the desktop app already owns ports 8088/8090, so this first command will fail with "address already in use." If no, carry on. In a terminal:
mkdir -p ~/givo-server/config ~/givo-server/recordings
docker run -d --name givo-server \
-p 8088:8088 -p 8090:8090 \
-v ~/givo-server/config:/config \
-v ~/givo-server/recordings:/recordings \
ghcr.io/gjdigitaldev/givo-server:latest
docker logs givo-server
What you should see: the first command prints nothing
(that's success). The docker run command downloads the server
the first time (a minute of progress lines), then prints one long string
of random letters and numbers — that string means it started. The
logs command prints the server's startup banner — your
pairing code is in it. To double-check it's running at any time, run
docker ps: you should see a givo-server row
whose STATUS starts with "Up".
On Windows, use these one-line versions in Command
Prompt instead (the multi-line \ style above is Mac/Linux
shell syntax):
mkdir "%USERPROFILE%\givo-server\config" "%USERPROFILE%\givo-server\recordings"
docker run -d --name givo-server -p 8088:8088 -p 8090:8090 -v "%USERPROFILE%\givo-server\config:/config" -v "%USERPROFILE%\givo-server\recordings:/recordings" ghcr.io/gjdigitaldev/givo-server:latest
With bridge networking the server is not auto-discovered — in the GiVo
app choose Add Server Manually and enter the computer's IP
address and port 8088.
Port 8088 already in use? (For example, the GiVo Server
desktop app is also running on the same machine.) Move the server to
another port — set GIVO_PORT and publish the same
numbers on both sides, keeping the second port at
API port + 2:
docker run -d --name givo-server \
-e GIVO_PORT=18088 \
-p 18088:18088 -p 18090:18090 \
-v ~/givo-server/config:/config \
-v ~/givo-server/recordings:/recordings \
ghcr.io/gjdigitaldev/givo-server:latest
The -e GIVO_PORT=18088 line is what moves the server itself
to the new port — don't drop it. Without it the server keeps listening on
8088 inside the container and the published 18088 port leads
nowhere, so the container runs but pairing can't connect. (Windows
one-line version:)
docker run -d --name givo-server -e GIVO_PORT=18088 -p 18088:18088 -p 18090:18090 -v "%USERPROFILE%\givo-server\config:/config" -v "%USERPROFILE%\givo-server\recordings:/recordings" ghcr.io/gjdigitaldev/givo-server:latest
Already ran the wrong command? No harm done — remove
the attempt and start over. This is always safe (your recordings and
settings live in the givo-server folders, not in the
container):
docker rm -f givo-server
Common errors, decoded:
bind: address already in use— something on this computer (usually the GiVo Server desktop app) already owns that port. Use the alternate-port command above.The container name "/givo-server" is already in use— a previous attempt is still there. Rundocker rm -f givo-server, then try again.command not found— only part of a command was pasted. Use the Copy button and paste the whole box.Cannot connect to the Docker daemon— Docker Desktop isn't running. Open it, wait for "running", try again.
Then pair by IP with port 18088. (Mismatched inside/outside
numbers — like -p 18088:8088 — will pair and record fine but
break casting, so prefer the same-number form above.)
3. Synology (Container Manager)
On DSM 7.2+ with Container Manager installed:
- Open Container Manager → Registry, search for
gjdigitaldev/givo-serveronghcr.ioand download thelatesttag. (If your DSM can't search ghcr, add it via Project → paste the compose file from section 1 instead — that's often the easier path.) - Create two shared-folder locations first in File Station, e.g.
/docker/givo-server/configand a recordings folder on your big volume, e.g./video/GiVo Recordings. - Container Manager → Container → Create from the
image. In the wizard:
- Network: select Use the same network as
Docker Host if available — auto-discovery and casting then just
work. Otherwise keep bridge and add port mappings
8088 → 8088and8090 → 8090(TCP, same numbers on both sides). - Volumes: map
/docker/givo-server/config→/configand your recordings folder →/recordings. - Environment: optionally add
GIVO_NAME,GIVO_PASSWORD(fixed pairing code), and the feature toggles from section 8. - Enable auto-restart.
- Network: select Use the same network as
Docker Host if available — auto-discovery and casting then just
work. Otherwise keep bridge and add port mappings
- Start the container, open its Log tab, and read the
pairing code from the banner. With bridge networking, pair via
Add Server Manually using the NAS IP and port
8088.
4. Unraid
Until a Community Applications template lands, add the container from
the Docker tab (Add Container) or a terminal. The equivalent
docker run:
docker run -d --name givo-server \
--network host \
-e GIVO_NAME="GiVo DVR" \
-v /mnt/user/appdata/givo-server:/config \
-v "/mnt/user/media/GiVo Recordings":/recordings \
--restart unless-stopped \
ghcr.io/gjdigitaldev/givo-server:latest
In the Add Container form that maps to: Repository
ghcr.io/gjdigitaldev/givo-server:latest, Network Type
Host, a path mapping for /config →
/mnt/user/appdata/givo-server, a path mapping for
/recordings → your media share, plus any
GIVO_* variables. Host networking is the Unraid-typical
choice and keeps auto-discovery + casting working; if you use a custom
bridge instead, publish 8088 and 8090 at the
same numbers and pair by IP. Read the pairing code with
docker logs givo-server (or the container's log icon in the
UI).
5. Native Linux binary (systemd)
Prefer no Docker? Download
GiVo-Server-Linux-amd64.tar.gz
or
GiVo-Server-Linux-arm64.tar.gz.
Each contains static binaries (the server plus the commercial-skip and
caption engines), a README with step-by-step instructions, and a
ready-made givo-server.service systemd unit. The only
requirement is ffmpeg from your distribution:
sudo apt install ffmpeg # Debian/Ubuntu (dnf/pacman equivalents work too)
tar xzf GiVo-Server-Linux-amd64.tar.gz && cd givo-server-linux-amd64
sudo useradd --system --home-dir /var/lib/givo-server --shell /usr/sbin/nologin givo
sudo mkdir -p /opt/givo-server /var/lib/givo-server/recordings
sudo cp givo-server comskip comskip.ini whisper-cli /opt/givo-server/
sudo chown -R givo:givo /var/lib/givo-server
sudo cp givo-server.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now givo-server
journalctl -u givo-server | grep "PAIRING CODE" | tail -1
Keep givo-server, comskip,
comskip.ini, and whisper-cli in the same
directory — the server finds the feature binaries beside itself. Point
--recordings-dir in the unit file at a big disk.
Updating the native binary (build 37 and later): the server updates itself in place —
sudo givo-server update --check # see what's available
sudo givo-server update # download, verify, swap & restart
The update command never interrupts a recording — if one is running it
waits and tells you to retry, and the previous binary is kept beside the
new one as givo-server.previous in case you ever want to
roll back. Prefer it fully automatic? The tarball ships an optional
givo-server-update.timer systemd unit (off by default) that
checks once a day — enable it with
sudo systemctl enable --now givo-server-update.timer. See
§6 of the bundled README for details.
6. Pairing — codes, rotation & fixed codes
The server prints a six-digit pairing code in its logs, inside a clearly marked banner:
docker logs givo-server
...
[Server] ==============================================================
[Server] GiVo DVR is ready.
[Server] PAIRING CODE: 481523
[Server] API: https://<this machine's LAN IP>:8088
...
On each iPhone, iPad, or Apple TV (same network):
- In GiVo Stream, open DVR → DVR Server.
- If the server appears automatically, select it. If not (normal with
bridge networking), choose Add Server Manually and enter
the machine's IP address and port
8088. - Enter the pairing code. Pairing is one-time per device.
The code rotates. For security, the random pairing code changes after every successful pairing — the code that paired your iPhone will not pair your Apple TV. Re-read the current code right before pairing each device:
Docker:
docker logs givo-server | grep "PAIRING CODE" | tail -1
systemd:
journalctl -u givo-server | grep "PAIRING CODE" | tail -1
(Each prints just the newest code — any codes above it in the full log are already used up.)
Pairing several devices? Set a fixed code — it never
rotates, so every device uses the same one. Pick your own six digits.
These are settings, not commands — a bare -e GIVO_PASSWORD=…
pasted on its own does nothing. Add the setting where your setup defines
the server, then restart it:
Docker Compose — un-comment (or add) the line in your
docker-compose.yml, then re-run
docker compose up -d:
environment:
- GIVO_PASSWORD=123456
docker run — a container's settings can't be changed
while it exists, so remove it and run it again with the extra
-e line included. Devices you already paired STAY paired
(pairings live in the config folder, which survives):
docker rm -f givo-server
⚠️ A run command replaces ALL of your settings — copy the block that matches YOUR setup (and keep any other lines yours uses).
Standard setup (port 8088):
docker run -d --name givo-server \
-e GIVO_PASSWORD=123456 \
-p 8088:8088 -p 8090:8090 \
-v ~/givo-server/config:/config \
-v ~/givo-server/recordings:/recordings \
ghcr.io/gjdigitaldev/givo-server:latest
Alternate-port setup from section 2 (the GiVo Server desktop app also runs on this computer):
docker run -d --name givo-server \
-e GIVO_PORT=18088 \
-e GIVO_PASSWORD=123456 \
-p 18088:18088 -p 18090:18090 \
-v ~/givo-server/config:/config \
-v ~/givo-server/recordings:/recordings \
ghcr.io/gjdigitaldev/givo-server:latest
systemd — add this line in the [Service]
section of givo-server.service, then
sudo systemctl daemon-reload && sudo systemctl restart givo-server:
Environment=GIVO_PASSWORD=123456
Treat the fixed code like a password for your DVR — anyone on your network who has it can pair.
7. Host vs. bridge networking
Host networking (Linux/NAS only) is the simple path: automatic discovery works, casting to AirPlay and Chromecast devices works, no port bookkeeping.
Bridge networking (Docker Desktop, Synology's default, custom bridges) also works — publish both ports at the same numbers and pair by IP:
ports:
- "8088:8088" # API
- "8090:8090" # casting relay (always API port + 2)
- Automatic discovery doesn't cross a bridge network — use Add Server Manually with the machine's IP.
- The casting relay must keep the
API port + 2spacing, and both mappings must use identical numbers on both sides (8088:8088,8090:8090) or casting won't work. Moving ports? SetGIVO_PORTand publish the new pair at the same numbers (see section 2). - Both ports are LAN-safe: the API uses TLS with certificate pinning plus per-device tokens, and the relay port is token-gated. Don't forward either to the internet.
8. Commercial skip & auto captions
The Linux build ships the same optional post-recording passes as the macOS and Windows apps: commercial skip (powered by Comskip) and automatic captions (speech-to-text via Whisper) for recordings without broadcast captions. Both are off by default — they are CPU-heavy passes that run after each recording finishes — and recordings themselves are never delayed by them.
Docker Compose — un-comment (or add) these lines in
your docker-compose.yml, then re-run
docker compose up -d:
environment:
- GIVO_COMMERCIAL_SKIP=true
- GIVO_WHISPER_CAPTIONS=true
- GIVO_WHISPER_MODEL=distil-large-v3
docker run — settings can't be added to an existing
container, so remove it and run it again with the extra lines (recordings,
pairings, and settings all survive — they live in your
givo-server folders):
docker rm -f givo-server
⚠️ A run command replaces ALL of your settings — copy the block that matches YOUR setup. If your command has other lines (a fixed pairing code, a different name), keep them too.
Standard setup (port 8088):
docker run -d --name givo-server \
-e GIVO_COMMERCIAL_SKIP=true \
-e GIVO_WHISPER_CAPTIONS=true \
-e GIVO_WHISPER_MODEL=distil-large-v3 \
-p 8088:8088 -p 8090:8090 \
-v ~/givo-server/config:/config \
-v ~/givo-server/recordings:/recordings \
ghcr.io/gjdigitaldev/givo-server:latest
Alternate-port setup from section 2 (the GiVo Server desktop app also runs on this computer):
docker run -d --name givo-server \
-e GIVO_PORT=18088 \
-e GIVO_COMMERCIAL_SKIP=true \
-e GIVO_WHISPER_CAPTIONS=true \
-e GIVO_WHISPER_MODEL=distil-large-v3 \
-p 18088:18088 -p 18090:18090 \
-v ~/givo-server/config:/config \
-v ~/givo-server/recordings:/recordings \
ghcr.io/gjdigitaldev/givo-server:latest
Caption model tiers (pick one with GIVO_WHISPER_MODEL;
approximate one-time download):
small.en— ~0.5 GB. Fastest, English only. Good for modest NAS CPUs.medium.en— ~1.5 GB. English only.distil-large-v3— ~1.5 GB. Default; best speed/quality balance, multilingual.large-v3— ~3 GB. Best quality, slowest.
First-enable behavior: the model downloads
automatically the first time captions are enabled — watch progress in the
container log. It lands in /config/models on your config
volume and is kept there permanently, so container updates and restarts
never re-download it. Captions appear on new recordings once the model is
ready. On modest CPUs a transcription can take a large fraction of the
recording's length; passes queue one at a time so recording is never
starved.
Commercial skip needs no download — enable it and skip points appear on new recordings (and can be added to older ones from the app). Comskip is GPL-licensed; its license and notice ship inside the image and tarballs.
9. Storage, backups & updates
/configis your server's identity — back it up, never delete it. It holds the database, the TLS keys your devices pin when they pair, and the downloaded caption models. As long ascert.pemandkey.pemsurvive, your devices never need to re-pair; lose them and every device must pair again./recordingsholds the recordings themselves. Size it generously.- Updating is the normal Docker flow —
docker compose pull && docker compose up -d. Your/configand/recordingscarry over untouched. The container shuts down gracefully (up to 90 seconds) so an in-progress recording is finalized, never truncated. - Automatic updates (optional): add
Watchtower to your
compose file and it pulls new GiVo Server images on a schedule —
for example checking daily at 5 AM, when a recording is least
likely to be running:
The 90-second grace period applies here too, so even an unlucky mid-recording update finalizes the recording before swapping.watchtower: image: containrrr/watchtower restart: unless-stopped volumes: - /var/run/docker.sock:/var/run/docker.sock command: --schedule "0 0 5 * * *" --cleanup givo-server - Native binary:
sudo givo-server update(build 37+) — see the update notes in section 5.
10. All settings
Every setting is available as an environment variable (handy in compose) or a command-line flag; flags win over environment:
GIVO_NAME— the server name shown in the app.GIVO_PASSWORD— fixed pairing code (otherwise a random code that rotates after each pairing).GIVO_PORT— API port (default8088; the casting relay always follows at port + 2).GIVO_RECORDINGS_DIR— recordings folder (the image defaults to/recordings).GIVO_DB_PATH— database location (the image defaults to/config/givo.db; TLS keys and caption models live beside it).GIVO_COMMERCIAL_SKIP—true/false, commercial-break detection after each recording.GIVO_WHISPER_CAPTIONS—true/false, automatic captions for recordings without broadcast captions.GIVO_WHISPER_MODEL—large-v3|distil-large-v3|medium.en|small.en.
Questions or stuck? We're happy to help — support@givostreamapp.com or the Discord.