diff --git a/scripts/docker/entrypoint-allinone.sh b/scripts/docker/entrypoint-allinone.sh
index a6062b32c..4f88b3382 100644
--- a/scripts/docker/entrypoint-allinone.sh
+++ b/scripts/docker/entrypoint-allinone.sh
@@ -212,11 +212,10 @@ run_migrations() {
echo "✅ Database migrations complete"
}
-# Run migrations on first start or when explicitly requested
-if [ ! -f /data/.migrations_run ] || [ "${FORCE_MIGRATIONS:-false}" = "true" ]; then
- run_migrations
- touch /data/.migrations_run
-fi
+# Always run migrations on startup - alembic upgrade head is safe to run
+# every time. It only applies pending migrations (never re-runs applied ones,
+# never calls downgrade). This ensures updates are applied automatically.
+run_migrations
# ================================================
# Environment Variables Info
diff --git a/surfsense_web/content/docs/docker-installation.mdx b/surfsense_web/content/docs/docker-installation.mdx
index ec8cb246b..22962143e 100644
--- a/surfsense_web/content/docs/docker-installation.mdx
+++ b/surfsense_web/content/docs/docker-installation.mdx
@@ -4,9 +4,6 @@ description: Setting up SurfSense using Docker
---
-
-# Docker Installation
-
This guide explains how to run SurfSense using Docker, with options ranging from quick single-command deployment to full production setups.
## Quick Start with Docker 🐳
@@ -126,6 +123,53 @@ docker rm surfsense
docker rm surfsense && docker volume rm surfsense-data
```
+### Updating
+
+To update SurfSense to the latest version, you can use either of the following methods:
+
+
+Your data is safe! The `surfsense-data` volume persists across updates, and database migrations are applied automatically on every startup.
+
+
+**Option 1: Manual Update**
+
+```bash
+# Stop and remove the current container
+docker rm -f surfsense
+
+# Pull the latest image
+docker pull ghcr.io/modsetter/surfsense:latest
+
+# Start with the new image
+docker run -d -p 3000:3000 -p 8000:8000 -p 5133:5133 \
+ -v surfsense-data:/data \
+ --name surfsense \
+ --restart unless-stopped \
+ ghcr.io/modsetter/surfsense:latest
+```
+
+**Option 2: Using Watchtower (one-time auto-update)**
+
+[Watchtower](https://github.com/nicholas-fedor/watchtower) can automatically pull the latest image, stop the old container, and restart it with the same options:
+
+```bash
+docker run --rm \
+ -v /var/run/docker.sock:/var/run/docker.sock \
+ nickfedor/watchtower \
+ --run-once surfsense
+```
+
+
+Use the `nickfedor/watchtower` fork. The original `containrrr/watchtower` is no longer maintained and may fail with newer Docker versions.
+
+
+If you used Docker Compose for the quick start, updating is simpler:
+
+```bash
+docker compose -f docker-compose.quickstart.yml pull
+docker compose -f docker-compose.quickstart.yml up -d
+```
+
---
## Full Docker Compose Setup (Production)
@@ -395,6 +439,20 @@ pgAdmin is included in the Docker setup to help manage your PostgreSQL database.
- Password: `postgres` (or your custom POSTGRES_PASSWORD)
6. Click "Save" to connect
+## Updating (Full Docker Compose)
+
+To update the full Docker Compose production setup to the latest version:
+
+```bash
+# Pull latest changes
+git pull
+
+# Rebuild and restart containers
+docker compose up --build -d
+```
+
+Database migrations are applied automatically on startup.
+
## Useful Docker Commands
### Container Management