Docker deployment¶
This page covers production Docker deployment in more detail than the installation guide.
Image¶
The official image is built from docker/Dockerfile and published to the GitHub Container Registry:
The image is based on ghcr.io/astral-sh/uv:python3.13-alpine and includes:
- Python 3.13 (via uv)
rsync— used bybackup.shandsync_backup_to_remote.shopenssh-client—ssh,ssh-keygen,ssh-keyscanfor key management and remote checkssshpass— used by the Connections page to install public keys on remote hostsbash— the shell scripts require bash, not sh
Volume mount¶
The single volume /data holds all persistent state:
/data/
├── backup_config.yml ← your backup configuration (includes schedule)
├── log.db ← SQLite run history
└── ssh/ ← generated SSH keypairs and known_hosts
├── ssh_config ← auto-generated SSH client config
├── known_hosts ← trusted remote host keys
└── <keyname> ← private keys (mode 600)
Mount your own directory here:
Do not lose this directory
All configuration, run history, SSH keys, and trusted host keys live here. Back it up, or at minimum keep your backup_config.yml in version control.
Environment variables¶
| Variable | Required | Description |
|---|---|---|
SECRET_KEY |
Yes (production) | Flask session secret — use a long random string |
ADMIN_USER |
No | Web UI username (default: admin) |
ADMIN_PASSWORD |
Yes (production) | Web UI password |
GMAIL_ADDRESS |
Yes | Gmail sender address |
GMAIL_PASSWORD |
Yes | Gmail app password — see setup guide |
DATA_ROOT |
No | Override the data directory (default: /data) |
APP_ENV |
No | production (default in Docker) or development |
Standard docker-compose.yml¶
A minimal production setup exposing the web UI directly on port 5003:
services:
mnemosynce:
image: ghcr.io/mark-me/mnemosynce:latest
container_name: mnemosynce
restart: unless-stopped
ports:
- "5003:5000"
volumes:
- /data/mnemosynce:/data
- /mnt/backup:/mnt/backup
env_file:
- .env
environment:
- APP_ENV=production
- TZ=Europe/Amsterdam
Store secrets in .env alongside the compose file:
SECRET_KEY=a-long-random-string
ADMIN_USER=admin
ADMIN_PASSWORD=change-me
GMAIL_ADDRESS=your.account@gmail.com
GMAIL_PASSWORD=your-16-char-app-password
Tailscale¶
If your remote backup sources or destinations are only reachable via Tailscale, the container must be able to route to Tailscale IP addresses (100.x.x.x). The simplest approach depends on whether the Docker host is itself a Tailscale node.
Host is a Tailscale node (recommended)¶
When the machine running Docker is already enrolled in your Tailscale network, use network_mode: host so the container shares the host's network namespace and inherits its Tailscale routes:
services:
mnemosynce:
image: ghcr.io/mark-me/mnemosynce:latest
container_name: mnemosynce
restart: unless-stopped
network_mode: host # shares host network — Tailscale IPs reachable
volumes:
- /data/mnemosynce:/data
- /mnt/backup:/mnt/backup
env_file:
- .env
environment:
- APP_ENV=production
- TZ=Europe/Amsterdam
Ports with network_mode: host
With network_mode: host the container binds directly to the host's port 5000. Remove any ports: mapping — it is ignored and unnecessary. Make sure nothing else on the host uses port 5000.
TSDProxy with network_mode: host
If you expose Mnemosynce via TSDProxy, set tsdproxy.hostname to the host's LAN IP so TSDProxy can reach the app. The tsdproxy.container_port label still works:
Host is not a Tailscale node¶
Run a Tailscale sidecar container and share its network namespace:
services:
tailscale:
image: tailscale/tailscale:latest
container_name: tailscale
environment:
- TS_AUTHKEY=tskey-auth-xxxx
- TS_STATE_DIR=/var/lib/tailscale
volumes:
- /var/lib/tailscale:/var/lib/tailscale
- /dev/net/tun:/dev/net/tun
cap_add:
- NET_ADMIN
- SYS_MODULE
restart: unless-stopped
extra_hosts:
- "pibackup:100.120.209.59"
- "desktop-ubuntu:100.82.72.45"
ports:
- "5003:5000"
mnemosynce:
image: ghcr.io/mark-me/mnemosynce:latest
container_name: mnemosynce
restart: unless-stopped
network_mode: "service:tailscale" # share Tailscale's network namespace
depends_on:
- tailscale
volumes:
- /data/mnemosynce:/data
- /mnt/backup:/mnt/backup
env_file:
- .env
environment:
- APP_ENV=production
- TZ=Europe/Amsterdam
Move ports: and extra_hosts: to the tailscale service since it owns the network namespace.
Trusting remote host keys over Tailscale¶
Regardless of which Tailscale approach you use, SSH connections to remote hosts require their host keys to be in known_hosts before a backup can sync. Do this once per host from the Settings → Connections page using the Trust host key panel — enter the hostname and click the button. The key is written to /data/ssh/known_hosts inside the persistent volume and survives container restarts.
Reverse proxy (nginx)¶
To expose Mnemosynce over HTTPS behind nginx:
server {
listen 443 ssl;
server_name backup.example.com;
ssl_certificate /etc/ssl/certs/backup.example.com.crt;
ssl_certificate_key /etc/ssl/private/backup.example.com.key;
location / {
proxy_pass http://localhost:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# Required for Server-Sent Events (live progress view)
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
}
}
SSE and proxy buffering
The live progress view uses Server-Sent Events. If your proxy buffers responses, the terminal feed will appear frozen. Set proxy_buffering off and proxy_read_timeout to at least 60 seconds.
Updating¶
The /data volume is preserved across image updates.