Maintenance and best practices
Backups
PostgreSQL database
Automatic backup: a Docker backup service (or a server cron job) can be configured to run pg_dump regularly.
Manual backup:
# On the server
pg_dump -h postgres -U postgres -d racines -F c -f /backups/racines_$(date +%Y%m%d).dump
Restore:
pg_restore -h postgres -U postgres -d racines /backups/racines_20260515.dump
Recommendations: - Daily backup minimum - 30-day retention minimum - Store backups off the same server (S3, remote MinIO, etc.) - Monthly restoration test
MinIO audio files
Audio files are stored in the audios bucket with versioning enabled (each re-recording creates a new version, the old one is preserved).
MinIO backup:
# Via mc (MinIO Client)
mc mirror minio/audios /backup/audios/
# Or via the provided npm script
npm run audio:download-originals
Monitoring
Application health check
The health endpoint is always available:
GET /api/health/live
Expected response (HTTP status 200):
{ "status": "ok", "timestamp": "2026-05-31T12:00:00Z" }
This endpoint is used by Docker (healthcheck every 30s) and can be monitored by an external tool (UptimeRobot, Grafana, etc.).
Application logs
Accessing logs via Docker:
# Live logs
docker logs -f racines-app
# Last 100 lines
docker logs --tail=100 racines-app
# Logs from the past hour
docker logs --since=1h racines-app
Patterns to watch for:
- 500 errors on /api/audio/upload routes
- AUDIO_CDN_URL is empty messages (missing environment variable)
- PostgreSQL connection errors (ECONNREFUSED, too many clients)
- CSP violations (Refused to connect in client logs)
Disk space
Monitor server disk space, particularly:
- /var/lib/docker/volumes/: PostgreSQL and MinIO data
- /backups/: pg_dump backups
df -h
du -sh /var/lib/docker/volumes/*
Updating the application
Via the CI/CD pipeline (recommended)
Any push to the develop (or main/production) branch triggers the GitLab pipeline which:
1. Lints + tests
2. Builds the Docker image
3. Pushes to GitLab Registry
4. Deploys via Ansible (SSH + docker pull + docker-compose up -d)
Via manual SSH (emergency)
# On the server
cd /opt/racines
docker-compose pull app
docker-compose up -d app
docker-compose ps # Check that all services are "Up"
After an update
Check:
1. GET /api/health/live → { "status": "ok" }
2. The homepage loads correctly
3. An audio plays from the CDN
4. The admin page is accessible
5. The speaker interface is accessible
Performance optimisation
PostgreSQL connection pool
The pool is configured at 20 maximum connections (file src/lib/postgres.ts). Under peak load, connections are queued (10s timeout). If you see connection timeout errors, increase the pool or check for slow queries.
Slow queries:
-- Long-running queries
SELECT pid, now() - pg_stat_activity.query_start AS duration, query
FROM pg_stat_activity
WHERE (now() - pg_stat_activity.query_start) > interval '5 seconds';
CDN audio cache
Make sure AUDIO_CDN_URL is set and correct. A well-configured CDN (MinIO accessible publicly or via a third-party CDN) is critical for audio performance.
Troubleshooting common issues
Audio files not loading
Checks:
1. Is the AUDIO_CDN_URL variable set? → docker inspect racines-app | grep AUDIO_CDN_URL
2. Is the CDN URL accessible from outside? → curl -I [AUDIO_CDN_URL]/test.mp3
3. Are MinIO CORS settings configured? → Is the Access-Control-Allow-Origin header present?
4. Is the CSP blocking the CDN domain? → Check AUDIO_CDN_DOMAIN in env vars
Admin page inaccessible
- Check that the app container is running:
docker-compose ps - Check logs:
docker logs racines-app --tail=50 - Test the health check:
curl http://localhost:3000/api/health/live - Check Traefik (reverse proxy):
docker logs traefik --tail=50
Excel import failing in production
- Check the file size (> 50MB?)
- Check the file's UTF-8 encoding
- Check app logs for the detailed error
Service Worker not updated for players
After a deployment, players may keep the old version cached. Solutions: - The application automatically detects the update and displays a "Mise à jour disponible" banner - The player must manually reload the page or click the banner - If the problem persists: the player must clear their browser cache
Security
Best practices
- Change admin passwords regularly (via
npm run create-adminor direct SQL) - Limit the number of administrators — each admin must have their own credentials
- Never commit
.envfiles to Git (already in.gitignore) - Regenerate
JWT_SECRETif you suspect a compromise — all active sessions are invalidated
Sensitive environment variables
All sensitive variables are stored in GitLab CI/CD secrets and in the .app_env file on the server (outside Git).
Critical variables never to expose:
- JWT_SECRET
- PG_PASSWORD
- MINIO_SECRET_KEY
- MINIO_ACCESS_KEY