Technical log · service migration
Migrating a legacy service to containers: recurring mistakes
Assumptions about state, networking, and startup that break the first version
The container starts on the laptop without a hitch. You push it to the cluster and within minutes it begins restarting. It is almost never a runtime problem: it is the legacy service that took for granted things that no longer exist in a pod. Absolute paths pointing to a disk that is not mounted, a configuration file that was written to /var/lib/app and now lives in an ephemeral layer, or a connection to a database that was resolved by the old server's hostname.
Before exposing traffic, it is worth going through three fronts. First, state: check what the process writes to disk and whether that survives a pod restart. Second, networking: list every outbound dependency and confirm that it resolves inside the cluster, not through an inherited hosts file. Third, startup: measure how long the service takes to accept the first real request, not to open the port.
That last point is the one that causes the most headaches. Many legacy services open the socket before they finish loading caches, indexes, or connection pools. If the readiness probe only checks that the port responds, it will send traffic to a process that is not ready yet, and you will see intermittent errors that do not show up locally. Liveness, for its part, should not restart a slow startup: separate the timings and let readiness do the work of holding back traffic.
Environment configuration is another point where the image breaks. If the service reads environment variables, paths, or credentials embedded in the build, every change forces a rebuild. It is better to move everything that changes between environments into variables or a mounted volume, and leave in the image only what is the same everywhere. That way the same artifact goes through staging and production without surprises.
None of this is textbook theory: these are the patterns that show up again and again when a service that ran for years on a VM moves to a cluster. The verification sequence before exposing traffic saves most of the cascading restarts.