#Operations
Day-to-day running of a SignetMail server: updating, monitoring, backups, restoring and renewing certificates. All commands run on the server in the folder with docker-compose.yml (for example /opt/signetmail/cloud).
#Daily checks (30 seconds)
curl -s https://<host>/health # {"status":"ok","lastSyncUtc":"…"} — sync time should be < ~2 h old
sudo docker compose ps # both services "running"
sudo docker compose logs --tail 100 signetmail-api
Warning signs: lastSyncUtc stuck in the past (Graph permissions or certificate problem), repeated errors in the log, a container restarting.
#Updating
- Back up first (see below).
- Pull and restart:
sudo docker login ghcr.io -u <github-user> # token with read:packages only sudo docker compose pull sudo docker compose up -d sudo docker logout ghcr.io - Verify
/healthand sign in to the portal. - Pull the repository (
git -C /opt/signetmail pull) when release notes mention changes todocker-compose.yml,Caddyfileor.env.example, and compare with your files.
To control versions, set SIGNETMAIL_VERSION=sha-<commit> in .env instead of latest; rolling back is then a matter of setting the previous value and running docker compose up -d.
The operating system patches itself (dnf-automatic). Reboot occasionally when the kernel was updated (sudo dnf needs-restarting -r).
#Backups
What to protect: the data/ folder (SQLite database with organizations, signatures, versions, rules, access, audit log, statistics and the uploads/ images), your .env, and certs/graph.pfx (or be ready to create a new certificate).
#Encrypted backups to a private GitHub repository
backup.sh creates a consistent snapshot (sqlite3 .backup), packs it, encrypts it with AES-256 (PBKDF2, 600 000 iterations) and pushes it to a private repository with a repository-specific deploy key. The last 30 encrypted copies are kept.
One-time setup:
- Create an empty private repository, for example
signetmail-backups. - On the server create a key used only for it:
Add the public key in the repository under Settings → Deploy keys → Add deploy key, with Allow write access.ssh-keygen -t ed25519 -N "" -f ~/.ssh/backup_deploy cat ~/.ssh/backup_deploy.pub - Create the encryption passphrase and store a copy in your password manager:
openssl rand -base64 32 > .backup-passphrase && chmod 600 .backup-passphrase echo 'BACKUP_GIT_REMOTE=git@github.com:<user>/signetmail-backups.git' >> .env sudo dnf -y install git # if git is missing ./backup.sh - Schedule it daily:
crontab -e 0 3 * * * /opt/signetmail/cloud/backup.sh
Warning. The passphrase is the only way to open a backup. Store it outside the server (password manager). Never paste it into chats or tickets; if it leaked, generate a new one — new backups use it, old ones stay readable with the old one.
Local copies also remain in backups/.
#Restoring
On a clean server (with the stack installed but stopped):
git clone git@github.com:<user>/signetmail-backups.git
openssl enc -d -aes-256-cbc -pbkdf2 -iter 600000 \
-in signetmail-<timestamp>.tar.gz.enc -pass file:.backup-passphrase | tar -xz
You get signetmail.db and uploads/. Then:
sudo docker compose down
sudo cp signetmail.db data/ && sudo cp -r uploads data/
sudo chown -R 1654 data
sudo docker compose up -d
Test a restore at least once, before you need it.
#Renewing the Graph certificate
certs/graph.cer expires 730 days after creation. Before that date:
- Generate a new pair: move the old
certs/graph.*away and run./setup.shagain. - In Entra → Certificates & secrets upload the new
graph.cer(keep the old one until the new one works). - Restart:
sudo docker compose restart signetmail-apiand check/health. - Remove the old certificate from Entra.
If it expires unnoticed the directory sync fails: lastSyncUtc stops advancing and changes in Entra ID (new employees, new titles) no longer reach signatures.
#TLS certificate
Caddy renews the Let's Encrypt certificate automatically. It needs ports 80 and 443 open and the DNS record intact. If the site shows a certificate warning, check sudo docker compose logs caddy.
#Changing configuration
Edit .env and run sudo docker compose up -d; containers whose settings changed are recreated. Examples: changing PORTAL_ALLOWED_IPS, switching the version, rotating GRAPH_CERT_PASSWORD (after re-running setup.sh).
#Moving to another server
- Take a backup and restore it on the new server (see above).
- Copy
.envandcerts/. - Switch the DNS record to the new IP. Caddy requests a new certificate for the new server.
- Update nothing in Entra if the host name is unchanged.
#Log locations
sudo docker compose logs [service] — rotated automatically (5 × 10 MB per container). The Windows agent logs to %LOCALAPPDATA%\SignetMail\agent.log on each PC.
#Monitoring suggestions
- An external uptime monitor on
https://<host>/health. - An alert if
lastSyncUtcis older than three hours. - A calendar reminder for the Graph certificate expiry.
- A calendar reminder to test the restore twice a year.