--- title: "Updating & Maintaining Containers" description: "How to safely update self-hosted tools running in Docker. Update workflows, rollbacks, and optional automation with Watchtower." --- # Updating & Maintaining Containers Your tools need updates — security patches, bug fixes, new features. But updating a self-hosted tool isn't like clicking "Update" in an app store. You need a process. ## The Safe Update Workflow Follow this **every time** you update a tool: ```bash # 1. Backup first (ALWAYS) docker exec my-db pg_dump -U postgres mydb > backup_$(date +%Y%m%d).sql # 2. Pull the new image docker compose pull # 3. Recreate containers with new image docker compose up -d # 4. Check logs for errors docker compose logs -f --tail=50 # 5. Verify the tool works curl -I https://app.yourdomain.com ``` > ⚠️ **Golden Rule:** Never update without a backup. If something breaks, you can roll back in 60 seconds. ## Rolling Back Something went wrong? Here's how to revert to the previous version: ### Option 1: Pin to Previous Version ```yaml # docker-compose.yml — change the tag services: app: image: plausible/analytics:v2.0.0 # Was :v2.1.0 ``` ```bash docker compose up -d ``` ### Option 2: Restore From Backup ```bash # Stop the broken service docker compose down # Restore the database backup cat backup_20260218.sql | docker exec -i my-db psql -U postgres mydb # Start with the old image docker compose up -d ``` ## Image Tags: `latest` vs Pinned Versions | Approach | Pros | Cons | |---|---|---| | `image: app:latest` | Always gets newest | Can break unexpectedly | | `image: app:v2.1.0` | Predictable, reproducible | Manual updates required | | `image: app:2` | Gets patches within major version | Some risk of breaking changes | > 🏆 **Our Recommendation:** Use **major version tags** (`image: postgres:16`) for databases and **pinned versions** (`image: plausible/analytics:v2.1.0`) for applications. Avoid `latest` in production. ## Automated Updates with Watchtower If you want hands-off updates (with some risk), **Watchtower** watches your containers and auto-updates them: ```yaml services: watchtower: image: containrrr/watchtower container_name: watchtower restart: unless-stopped volumes: - /var/run/docker.sock:/var/run/docker.sock environment: WATCHTOWER_CLEANUP: "true" WATCHTOWER_SCHEDULE: "0 0 4 * * *" # 4 AM daily WATCHTOWER_NOTIFICATIONS: "email" command: --include-restarting ``` ### Watchtower Caveats - It updates **all** containers by default. Use labels to control which ones: ```yaml services: plausible: image: plausible/analytics:latest labels: - "com.centurylinklabs.watchtower.enable=true" database: image: postgres:16 labels: - "com.centurylinklabs.watchtower.enable=false" # NEVER auto-update databases ``` - It doesn't run migrations. Some tools need `docker exec app migrate` after updates. - It can't roll back automatically. > ⚠️ **Never auto-update databases.** Postgres, MySQL, and Redis major version upgrades require manual migration steps. Always pin database images. ## Cleanup: Reclaiming Disk Space Old images pile up. Docker doesn't clean them automatically: ```bash # See how much space Docker is using docker system df # Remove unused images (safe) docker image prune -f # Nuclear option: remove ALL unused data docker system prune -a -f --volumes # ⚠️ This deletes stopped containers, unused images, AND orphaned volumes ``` ### Automate Cleanup Add to your crontab: ```bash # Weekly cleanup at 3 AM Sunday 0 3 * * 0 docker image prune -f >> /var/log/docker-cleanup.log 2>&1 ``` ## Update Checklist Before updating any tool: - [ ] Database backed up - [ ] Current version noted (in case of rollback) - [ ] Changelog reviewed for breaking changes - [ ] `.env` file backed up - [ ] Update applied and logs checked - [ ] Service verified working ## Next Steps → [Backups That Actually Work](/concepts/backups) — Make sure you can actually roll back → [Monitoring & Observability](/concepts/monitoring) — Catch failed updates automatically