Chapter 5: Infrastructure Upgrades
Chapter 5: Infrastructure Upgrades
Last Updated: 2026-03
5.2 Pre-Upgrade Checklist
Run this checklist before every upgrade, regardless of scope:
cd ITI/infrastructure/n8n-dify
# 1. Run backup
bash backup.sh
# 2. Verify backup files are non-zero
ls -lh /Users/username/Cursor/Archives/ | grep $(date +%Y%m%d)
# 3. Capture resource baseline
docker stats --no-stream > /Users/username/Cursor/Archives/docker-stats-pre-upgrade-$(date +%Y%m%d).log
# 4. Run smoke tests
cd tests
python -m pytest test_docker_health.py test_n8n_webhooks.py -m smoke -v
# 5. Record current image versions
cd ..
docker compose images
# 6. Check that no critical workflows are currently executing
# Open http://localhost:5678 > Executions > verify no active runs
All 6 steps must complete without errors. If any step fails, do not proceed with the upgrade.
5.3 Routine Image Updates (Monthly)
Most images use :latest or minor-version tags. A monthly pull keeps them current:
cd ITI/infrastructure/n8n-dify
docker compose pull
docker compose up -d
Then run the post-upgrade verification (Section 5.7).
5.4 Upgrading n8n
n8n uses n8nio/n8n:latest. A docker compose pull upgrades it automatically.
Special considerations
Encryption key: N8N_ENCRYPTION_KEY in .env must never change. It encrypts all stored credentials. There is no migration path if this key is changed — all credentials become permanently unrecoverable.
DB migrations: n8n runs internal database migrations on startup. After an upgrade, watch the logs:
docker compose logs -f iti-n8n | head -50
Look for migration completion messages before using the UI.
Workflow activation: Major n8n version upgrades may deactivate workflows. After upgrading:
# Count active workflows via API
curl -s http://localhost:5678/api/v1/workflows?active=true \
-H "X-N8N-API-KEY: <your-api-key>" | python3 -m json.tool | grep '"id"' | wc -l
Compare this count to the pre-upgrade baseline. If workflows were deactivated, re-publish each one through the n8n UI or API.
5.5 Upgrading Dify
Dify runs three coordinated images: dify-api, dify-worker, and dify-web. These must always be the same version.
Pinned vs latest images
| Image | Strategy | Notes |
|---|---|---|
dify-api |
:latest in development |
May pin in production |
dify-worker |
Same as dify-api | Must match exactly |
dify-web |
Same as dify-api | Must match exactly |
dify-sandbox |
Pinned (0.2.14) |
Do not update without release note review |
dify-plugin-daemon |
Pinned (0.5.3-local) |
Do not update without release note review |
Upgrade procedure
# 1. Check Dify release notes at https://github.com/langgenius/dify/releases
# 2. Update pinned tags in docker-compose.yml if necessary
# 3. Pull and restart
docker compose pull iti-dify-api iti-dify-worker iti-dify-web
docker compose up -d
# 4. Verify Dify console loads
curl -s -o /dev/null -w "%{http_code}" http://localhost:3000
# 5. Verify API proxy
curl -s -o /dev/null -w "%{http_code}" http://localhost:3001/console/api/setup
# 6. Test KB retrieval
# Open Dify UI > Knowledge > Retrieval Testing
5.6 Upgrading PostgreSQL (Major Version)
This is the highest-risk upgrade. Perform it only when explicitly needed. Major version upgrades (e.g., pg16 → pg17) require a full dump-and-restore because PostgreSQL data formats are not forward-compatible.
Procedure
cd ITI/infrastructure/n8n-dify
# Step 1: Full backup of all three databases
docker exec iti-postgres pg_dump -U postgres n8n > /Users/username/Cursor/Archives/n8n-db-pre-pgupgrade.sql
docker exec iti-postgres pg_dump -U postgres dify > /Users/username/Cursor/Archives/dify-db-pre-pgupgrade.sql
docker exec iti-postgres pg_dump -U postgres dify_plugin > /Users/username/Cursor/Archives/dify-plugin-db-pre-pgupgrade.sql
# Step 2: Stop the stack
docker compose down
# Step 3: Update the image in docker-compose.yml
# Change: pgvector/pgvector:pg16
# To: pgvector/pgvector:pg17
# Step 4: Remove the old data volume
docker volume rm n8n-dify_postgres_data
# Step 5: Start only Postgres (let it initialize)
docker compose up -d iti-postgres
# Wait for healthy status
docker compose ps iti-postgres
# Step 6: Restore all databases
docker exec -i iti-postgres psql -U postgres < /Users/username/Cursor/Archives/n8n-db-pre-pgupgrade.sql
docker exec -i iti-postgres psql -U postgres < /Users/username/Cursor/Archives/dify-db-pre-pgupgrade.sql
docker exec -i iti-postgres psql -U postgres < /Users/username/Cursor/Archives/dify-plugin-db-pre-pgupgrade.sql
# Step 7: Verify pgvector extension
docker exec iti-postgres psql -U postgres -d dify -c \
"SELECT extversion FROM pg_extension WHERE extname='vector';"
# Step 8: Bring up full stack
docker compose up -d
# Step 9: Run full test suite
cd tests
python -m pytest -v
5.7 Post-Upgrade Verification (Mandatory)
Run after every upgrade before considering it complete:
cd ITI/infrastructure/n8n-dify
# 1. All containers healthy
docker compose ps
# 2. Smoke tests
cd tests
python -m pytest test_docker_health.py test_n8n_webhooks.py -m smoke -v
# 3. Integration tests
python -m pytest test_n8n_workflows.py test_postgres_pgvector.py -v
# 4. Verify n8n workflow count matches pre-upgrade
# 5. Verify Dify KBs accessible (Dify UI > Knowledge)
# 6. Check n8n Executions for new errors post-upgrade
# 7. Compare docker stats to pre-upgrade baseline
docker stats --no-stream
5.8 Rollback Procedures
Rollback a single service
# 1. Stop the service
docker compose stop iti-n8n
# 2. Edit docker-compose.yml to pin the previous image version
# Change: n8nio/n8n:latest
# To: n8nio/n8n:1.XX.X (the version from pre-upgrade docker compose images output)
# 3. Restart
docker compose up -d iti-n8n
Full stack rollback
# 1. Stop everything
docker compose down
# 2. Restore databases from pre-upgrade backups
docker exec -i iti-postgres psql -U postgres -d n8n < /Users/username/Cursor/Archives/n8n-db-YYYYMMDD.sql
docker exec -i iti-postgres psql -U postgres -d dify < /Users/username/Cursor/Archives/dify-db-YYYYMMDD.sql
# 3. Revert image tags in docker-compose.yml to previous versions
# 4. Restart
docker compose up -d
5.9 Version Pinning Strategy
| Image | Strategy | Reason |
|---|---|---|
pgvector/pgvector:pg16 |
Pinned to major | Major version breaks require dump/restore |
redis:7-alpine |
Pinned to major | Redis AOF compatibility |
dify-sandbox:0.2.14 |
Pinned exactly | API surface changes with versions |
dify-plugin-daemon:0.5.3-local |
Pinned exactly | Plugin compatibility |
n8nio/n8n:latest |
Latest (dev) / pinned (prod) | Active development; migrations handle upgrades |
langgenius/dify-api:latest |
Latest (dev) / pinned (prod) | Same; Dify handles migrations |
Tip: After each upgrade, record the specific image versions from
docker compose imagesin a comment block at the top ofdocker-compose.yml. This makes rollback much faster.
Previous: Chapter 4 — Daily Operations | Next: Chapter 6 — PostgreSQL & pgvector
