Service Host Migration

A self-hosted platform migration from a constrained Docker VM to a dedicated service host, with Git-backed configuration, monitoring, backups, rollback posture, and a restore drill.

About this page. This is a public version of the migration story. It leaves out hostnames, addresses, credential paths, and private service URLs on purpose.

Why move it

The old host was doing too many jobs.

The original Docker host started as a simple place for personal services, dashboards, and automation. Over time it became the default landing zone for organizer tools, monitoring, analytics, media helpers, and AI-adjacent experiments. That worked, but it concentrated too much operational responsibility on a small virtual machine.

The goal of the migration was not just to move containers. It was to make the platform easier to rebuild: configuration in Git, clear stack ownership, fewer stale manual checkouts, explicit persistent state, and backup coverage that had been tested instead of merely hoped for.

Target state

A dedicated service host, organized by stack.

The current lab uses Docker Compose stacks grouped by function, with Komodo as the deployment control plane and Git as the durable source of truth. The service host now carries the application platform: dashboards, monitoring, organizer tools, document management, password vault, analytics, media services, photos, AI tools, and supporting databases and caches.

AreaRole in the platform
InfrastructureHomepage, uptime monitoring, update visibility, speed tests, and small custom widgets for operational context.
OrganizerBookmarks, tasks, calendar and contacts, document management, password vault, and supporting search/database services.
AI toolsModel routing, workflow automation, agent memory services, and web UI tooling for local experiments.
Media and photosWatch list, music, media server, photo library, and the state needed to keep them portable.
BackupsBackrest and Restic plans covering application state, database data, configuration, and selected host-local recovery material.

The public version intentionally describes categories, not internal addresses. The useful engineering detail is the operating model: Git-backed stacks, explicit state roots, monitored services, and verified recovery.

Cutover discipline

Move deliberately, leave a rollback path.

The migration was handled as a series of scoped moves rather than a single dramatic cutover. Staged services were verified at the application layer, routing changed only after destination checks passed, and source containers or data were stopped rather than deleted. That left a rollback path while the destination proved itself.

  • Control plane first. Live Compose labels and run directories were inspected before treating any file as authoritative.
  • Canonical Git next. Stack definitions were reconciled into a repository and pushed only after validation.
  • State made explicit. Persistent application data was moved out of ad hoc working directories into clearer app-state roots.
  • Stale copies archived. Old manual stack checkouts were archived rather than deleted during the cutover window.
  • Health checks before closure. Endpoints, container health, dashboard widgets, and monitoring routes were checked after each meaningful move.
Recovery proof

Backups are only real after a restore drill.

After the service host became the center of gravity, backup coverage was updated to include the new layout. A fresh Backrest/Restic snapshot was triggered and verified to include the new application-state paths.

The closure test was a Vaultwarden restore drill into isolated temporary storage. The drill restored the password-vault state only, verified the required database and key files, ran SQLite integrity checks, and compared representative table counts between production and restored copies without printing private vault contents.

Restore target
Isolated temporary storage, not the production service path
Files checked
SQLite database plus public/private RSA key files
Integrity check
Production and restored databases both returned ok
Data comparison
Representative table counts matched exactly
Cleanup
Temporary restore data and one-time credential files were removed afterward
Lessons

The migration was less about Docker than discipline.

  1. Running is not the same as recoverable. A service can be healthy while its Compose file, secrets, or data paths are not yet documented enough to rebuild.
  2. Live labels beat assumptions. Docker labels and mounted paths showed which files and directories were actually in use, including stale or nested checkouts that looked plausible but were not canonical.
  3. Backups need application context. Database-backed services need logical dumps or app-aware validation, not only raw file copies.
  4. Rollback posture lowers drama. Stop and archive before deleting. Keep source state until the destination has survived verification and backup cycles.
  5. A restore drill changes the confidence level. Seeing the restored database pass integrity checks and match production counts is qualitatively different from seeing a green backup job.
Related pages
  1. Engineering LabThe overview of the lab and its main systems.
  2. ProxmoxThe hypervisor and network base.
  3. Docker HostThe original Docker VM page, kept as historical setup notes.
  4. AI Homepage case studyUsing AI assistance to configure a self-hosted dashboard.