Affected files: .obsidian/workspace.json 2 Personal/Home Lab/Backup System - Kopia Server Setup.md
18 KiB
title, created_date, updated_date, aliases, tags
| title | created_date | updated_date | aliases | tags |
|---|---|---|---|---|
| Backup System - Kopia Server Setup | 2026-04-20 | 2026-04-20 |
Backup System - Kopia Server Setup
Overview
This document describes the setup we built for a self-hosted Kopia backup server in the homelab.
Final architecture
- Hypervisor: Proxmox
- Backup server runtime: Debian VM on Proxmox (IP:192.168.1.54 - IP managed by pi-hole)
- Backup repository storage: Synology NAS via NFS
- Backup service: Kopia Repository Server
- Remote/private access: intended via Tailscale or LAN
- First client: MacBook
- TLS: self-generated Kopia TLS certificate
Why this architecture was chosen
We first tried to run Kopia in an LXC container. That led to multiple issues:
- NFS mount permission issues with an unprivileged LXC
- AppArmor / systemd problems with newer Debian / systemd in LXC
- black Proxmox console / awkward LXC behavior
Because of that, we switched to a VM, which is the cleaner and more robust setup for this use case.
Final design
Components
- Debian VM runs Kopia Repository Server
- Synology NAS stores the actual backup repository blobs
- Kopia clients connect to the Repository Server over HTTPS
- MacBook connects as
claudio@macbook-main
Repository model
Important distinction:
- The repository itself is stored on the NAS filesystem
- The Repository Server is the HTTP/HTTPS layer in front of it
- Users connect to the server, not directly to the NAS share
This is better than mounting the share directly on each laptop because:
- clients do not depend on a mounted NAS path
- you get per-user accounts on the server
- easier multi-user setup
- easier remote use over VPN/Tailscale
Synology setup
Shared folder
A dedicated shared folder was used on Synology:
kopia-repository
NFS export
The NFS export path used was:
192.168.1.34:/volume1/kopia-repository
Synology NFS settings
Recommended / used settings:
- Privilege: Read/Write
- Squash: No mapping
- Security: sys
- Allow connections from non-privileged ports: enabled if needed
Notes:
Squash: No mappingis fine for a dedicated Kopia repository share.- In the LXC attempt, permissions still failed because of UID/GID mapping issues with unprivileged containers.
Failed LXC attempt and why we abandoned it
We first tried to run Kopia in a Proxmox LXC.
What we tried
- Created an LXC
- Tried mounting NFS inside the LXC
- Then switched to mounting NFS on the Proxmox host and bind-mounting it into the LXC
- Added
/dev/net/tunfor Tailscale / ZeroTier - Enabled
nesting=1because of Debian/systemd issues
Problems encountered
1. Wrong mount point created via Proxmox UI
We accidentally created an extra LXC disk instead of a bind mount.
We had this wrong line:
mp0: local-lvm:vm-102-disk-1,mp=/mnt/pve/kopia-repo,size=8G
This was not a bind mount from the host.
The correct bind mount syntax was:
mp0: /mnt/pve/kopia-repo,mp=/srv/kopia-repo
2. Console issues / black screen
The LXC booted, but the Proxmox console was black.
pct enter worked, but pct console did not work reliably.
3. systemd / AppArmor issues
We saw warnings such as:
Systemd 257 detected. You may need to enable nesting.- AppArmor denials related to
userns_create,mount,journald,networkd, etc.
This was partially fixed with:
pct set 102 --features nesting=1
pct restart 102
4. NFS permission issues in unprivileged LXC
Even when the share was mounted correctly, writes failed inside the container:
touch /srv/kopia-repo/testfile
# Permission denied
This happened because unprivileged container root is UID-mapped and Synology/NFS did not allow writes the way we wanted.
Conclusion
The LXC route was abandoned because it caused unnecessary complexity for a simple service.
We switched to a Debian VM, which is simpler and more maintainable.
VM setup
Assumptions
- Debian VM on Proxmox
- VM has network access to the Synology NAS
- NFS share exists on Synology
- Kopia repository will live on the NAS
Base packages installed
On the VM, we installed the following:
apt update
apt install -y nfs-common curl ca-certificates gnupg nano
NFS mount inside the VM
Mountpoint creation
mkdir -p /srv/kopia-repo
mkdir -p /var/lib/kopia
chmod 700 /var/lib/kopia
Manual test mount
mount -t nfs 192.168.1.34:/volume1/kopia-repository /srv/kopia-repo
Validation commands
df -h /srv/kopia-repo
touch /srv/kopia-repo/testfile
ls -l /srv/kopia-repo/testfile
rm /srv/kopia-repo/testfile
At this stage, the VM approach worked properly.
Persistent mount in /etc/fstab
We added:
192.168.1.34:/volume1/kopia-repository /srv/kopia-repo nfs defaults,_netdev 0 0
Then tested with:
umount /srv/kopia-repo
mount -a
df -h /srv/kopia-repo
Kopia installation on the VM
APT repo setup
We used the official Kopia apt repository.
Commands used:
install -d -m 0755 /etc/apt/keyrings
curl -s https://kopia.io/signing-key | gpg --dearmor -o /etc/apt/keyrings/kopia-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kopia-keyring.gpg] http://packages.kopia.io/apt/ stable main" > /etc/apt/sources.list.d/kopia.list
apt update
apt install -y kopia
Verify installation
kopia --version
Kopia repository creation
Repository directory
mkdir -p /srv/kopia-repo/repository
Create repository
We created a filesystem repository on the NFS-backed path:
kopia repository create filesystem --path=/srv/kopia-repo/repository
During this step, a repository password was chosen.
This password is critical. It encrypts the repository contents.
Environment variables
We used an environment file with exported variables for passwords.
Important shell note:
"$VAR"expands the variable'$VAR'does not expand the variable
So this is wrong:
--server-password='$KOPIA_SRV_PW'
And this is correct:
--server-password="$KOPIA_SRV_PW"
Variables used
Example variables:
export KOPIA_REPO_PW="..."
export KOPIA_SRV_CTRL_PW="..."
export KOPIA_SRV_PW="..."
And for manual startup we exported:
export KOPIA_PASSWORD="$KOPIA_REPO_PW"
First manual Kopia server start attempts
Initial attempt without TLS
We first tried something like:
kopia server start \
--address=0.0.0.0:51515 \
--server-control-username=server-control \
--server-control-password="$KOPIA_SRV_CTRL_PW" \
--server-username=kopia \
--server-password="$KOPIA_SRV_PW"
This failed with the message:
TLS not configured. To start server without encryption pass --insecure
So the server did not actually listen on the port.
Working manual TLS start
The working startup command was:
kopia server start \
--tls-generate-cert \
--tls-cert-file ~/my.cert \
--tls-key-file ~/my.key \
--address 0.0.0.0:51515 \
--server-control-username control
Notes:
- This generated a self-signed TLS cert and key.
- After generation, future starts must not use
--tls-generate-certagain.
Adding the first Kopia user
We created the first Mac user with:
kopia server user add claudio@macbook-main
This prompts for a password for that user.
This user/password is what the Mac client uses to connect.
Server refresh issue and root cause
We saw this error:
kopia server refresh ...
400 Bad Request: not connected
And from the Mac / KopiaUI we saw:
not connected to a direct repository
Root cause
The Repository Server was starting, but the process running under systemd was not connected to the repository.
This happened because:
- the repository had been created and connected as user
cef - but the systemd service was running as root by default
- root did not have the Kopia repository config/session
Fix
We changed the systemd service to run as user cef.
That solved the issue.
TLS certificate handling
Move cert and key to stable location
We moved the generated files out of the home directory:
sudo mkdir -p /etc/kopia
sudo mv ~/my.cert /etc/kopia/server.cert
sudo mv ~/my.key /etc/kopia/server.key
sudo chmod 600 /etc/kopia/server.key
sudo chmod 644 /etc/kopia/server.cert
Important permission fix
Because the service runs as user cef, that user needed access to the cert and key:
sudo chown cef:cef /etc/kopia/server.cert /etc/kopia/server.key
sudo chmod 600 /etc/kopia/server.key
sudo chmod 644 /etc/kopia/server.cert
Environment file for systemd
We created:
sudo nano /etc/kopia-server.env
Contents:
KOPIA_PASSWORD=YOUR_REPOSITORY_PASSWORD
KOPIA_SRV_CTRL_PW=YOUR_SERVER_CONTROL_PASSWORD
KOPIA_SRV_PW=YOUR_WEB_UI_PASSWORD
Permissions:
sudo chown root:cef /etc/kopia-server.env
sudo chmod 640 /etc/kopia-server.env
Final systemd service
We created:
sudo nano /etc/systemd/system/kopia-server.service
Final service:
[Unit]
Description=Kopia Repository Server
After=network-online.target remote-fs.target
Wants=network-online.target
Requires=remote-fs.target
[Service]
Type=simple
User=cef
Group=cef
EnvironmentFile=/etc/kopia-server.env
ExecStart=/usr/bin/kopia server start \
--tls-cert-file=/etc/kopia/server.cert \
--tls-key-file=/etc/kopia/server.key \
--address=0.0.0.0:51515 \
--server-control-username=control \
--server-control-password=${KOPIA_SRV_CTRL_PW} \
--server-username=kopia \
--server-password=${KOPIA_SRV_PW}
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Enable and start
sudo systemctl daemon-reload
sudo systemctl enable --now kopia-server
sudo systemctl status kopia-server --no-pager
Check if listening
ss -ltnp | grep 51515
Certificate fingerprint
Because the TLS certificate is self-generated, clients must trust it using the SHA256 fingerprint.
Get fingerprint
On the VM:
openssl x509 -in /etc/kopia/server.cert -noout -fingerprint -sha256 | sed 's/://g' | cut -f 2 -d =
Save the resulting fingerprint.
Refreshing server credentials
Once the service was working, refresh needed to use:
- HTTPS
- control username/password
- server certificate fingerprint
Command:
kopia server refresh \
--address=https://127.0.0.1:51515 \
--server-control-username=control \
--server-control-password="$KOPIA_SRV_CTRL_PW" \
--server-cert-fingerprint=YOUR_FINGERPRINT
If you get not connected, the server process is not connected to the repository context. Check that the service runs as the same user that has a valid Kopia repository config.
MacBook setup
Install Kopia on macOS
We used Homebrew:
brew install kopia
brew install kopiaui
Connect the Mac to the Repository Server
We used the CLI first, because it is more reliable for initial connection than KopiaUI.
Command:
kopia repository connect server \
--url=https://YOUR_VM_IP:51515 \
--server-cert-fingerprint=YOUR_CERT_FINGERPRINT \
--override-username=claudio \
--override-hostname=macbook-main
The login password here is the password for the Kopia server user:
claudio@macbook-main
Verify connection
kopia repository status
MacBook first test backup
Create a test folder
mkdir -p ~/kopia-test
echo "hello kopia" > ~/kopia-test/file1.txt
date > ~/kopia-test/file2.txt
Create first snapshot
kopia snapshot create ~/kopia-test
List snapshots
kopia snapshot list
Test restore
mkdir -p ~/kopia-restore-test
kopia restore latest ~/kopia-restore-test
ls -la ~/kopia-restore-test
This validates the full chain:
- VM
- NFS mount
- repository
- Kopia Repository Server
- Mac client connection
- backup
- restore
Automatic backup on the Mac
Basic idea
The recommended model is:
- connect the Mac to the repository once
- define backup roots
- use
kopia snapshot create --allon a schedule
Suggested first backup roots
Start simple. For example:
~/Documents~/Desktop- local project folders
Do not immediately back up all of ~/Library.
Initial snapshots for real backup roots
Example:
kopia snapshot create ~/Documents
kopia snapshot create ~/Desktop
Set retention / scheduling policy
Example:
kopia policy set ~/Documents \
--snapshot-interval=12h \
--keep-latest=14 \
--keep-daily=14 \
--keep-weekly=8 \
--keep-monthly=12
kopia policy set ~/Desktop \
--snapshot-interval=12h \
--keep-latest=14 \
--keep-daily=14 \
--keep-weekly=8 \
--keep-monthly=12
Robust automatic execution with launchd
Create:
~/Library/LaunchAgents/com.claudio.kopia-backup.plist
Contents:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.claudio.kopia-backup</string>
<key>ProgramArguments</key>
<array>
<string>/opt/homebrew/bin/kopia</string>
<string>snapshot</string>
<string>create</string>
<string>--all</string>
<string>--no-progress</string>
</array>
<key>StartInterval</key>
<integer>21600</integer>
<key>RunAtLoad</key>
<true/>
<key>StandardOutPath</key>
<string>/tmp/kopia-backup.out</string>
<key>StandardErrorPath</key>
<string>/tmp/kopia-backup.err</string>
</dict>
</plist>
Load it:
launchctl unload ~/Library/LaunchAgents/com.claudio.kopia-backup.plist 2>/dev/null || true
launchctl load ~/Library/LaunchAgents/com.claudio.kopia-backup.plist
launchctl list | grep kopia
This runs every 6 hours (21600 seconds).
Verify automatic backup
kopia snapshot list
And occasionally:
kopia restore latest ~/kopia-restore-test-2
How to add another user, for example your spouse
The model is:
- create a new user on the Kopia server
- connect that user’s machine with a fixed username/hostname identity
- create backup sources from that machine
Example: spouse on Windows
Suppose you want:
- username:
partner - hostname:
windows-laptop
1. Create the user on the Kopia server VM
On the VM:
kopia server user add partner@windows-laptop
Set a password when prompted.
2. Refresh credentials if needed
kopia server refresh \
--address=https://127.0.0.1:51515 \
--server-control-username=control \
--server-control-password="$KOPIA_SRV_CTRL_PW" \
--server-cert-fingerprint=YOUR_FINGERPRINT
3. Install Kopia on the spouse’s machine
For Windows, install Kopia / KopiaUI from the official installer.
4. Connect that machine to the server
From CLI, the equivalent pattern is:
kopia repository connect server \
--url=https://YOUR_VM_IP:51515 \
--server-cert-fingerprint=YOUR_CERT_FINGERPRINT \
--override-username=partner \
--override-hostname=windows-laptop
Then enter the password for:
partner@windows-laptop
5. Create first backup sources on that machine
For example on Windows:
- Documents
- Desktop
- Pictures
6. Run first backup and test restore
Do exactly the same test pattern as on the Mac:
- create first snapshot
- list snapshots
- restore a test folder
Naming convention recommendation
Use stable names so future maintenance is simple.
Examples:
claudio@macbook-mainclaudio@windows-mainpartner@windows-laptoppartner@android-photosif you ever use a separate flow later
Maintenance and troubleshooting
Check service status
sudo systemctl status kopia-server --no-pager
View Kopia server logs
sudo journalctl -u kopia-server -n 100 --no-pager
Check if port is listening
ss -ltnp | grep 51515
Check repository status as service user
kopia repository status
If this fails under the service user context, the Repository Server will not work correctly.
Test local HTTPS endpoint
curl -k https://127.0.0.1:51515/
Get certificate fingerprint again
openssl x509 -in /etc/kopia/server.cert -noout -fingerprint -sha256 | sed 's/://g' | cut -f 2 -d =
Common failure: wrong shell quoting
Wrong:
--server-password='$KOPIA_SRV_PW'
Correct:
--server-password="$KOPIA_SRV_PW"
Common failure: server not connected
Symptoms:
400 Bad Request: not connected- Mac / UI error:
not connected to a direct repository
Fix:
- make sure systemd service runs as the same user that created / connected the repository
- in this setup that was
cef
Common failure: browser works but Mac client fails
This usually means:
- HTTPS listener is fine
- web auth is fine
- but the server has no repository connection or client trust settings are wrong
Check:
kopia repository status- systemd service user
- cert fingerprint on client
Important files in this setup
On the VM
- Repository storage:
/srv/kopia-repo/repository
- NFS mountpoint:
/srv/kopia-repo
- TLS cert:
/etc/kopia/server.cert
- TLS key:
/etc/kopia/server.key
- systemd env file:
/etc/kopia-server.env
- systemd service:
/etc/systemd/system/kopia-server.service
On the Mac
- launchd job:
~/Library/LaunchAgents/com.claudio.kopia-backup.plist
What not to forget later
- Do not rerun
--tls-generate-certon every start. - Keep the repository password safe.
- Keep the server control password safe.
- Keep the Web UI password safe.
- Keep the certificate fingerprint documented.
- Test restores periodically, not just backups.
- Do not assume browser access means repository connectivity is correct.
Suggested next steps
- Finalize the Mac backup roots
- Test automatic backups from the Mac
- Add spouse’s machine as a second user
- Test restore from spouse’s machine too
- Later consider offsite replication of the Kopia repository
- Keep Time Machine in parallel on the Mac if you want easier full-machine restore