Me
— · Cloud · 6 min read

Why move from “traditional hosting” to Docker (and when you shouldn't)

Thumbnail

Choosing where to run your app used to mean renting a server (or a slice of one), installing packages, and carefully hand-tuning configs. That model still works—but Docker changes the game for how you build, ship, and run software.

If you're “not really sure what the correct phrase is,” traditional hosting usually means one of these:

  • Shared hosting / cPanel (multiple customers on the same box).
  • A single VPS or dedicated server you configure by hand (LAMP/LEMP, system packages, services).
  • A managed box where the provider hides some of the plumbing, but you still treat the server as a unique snowflake.

Docker replaces that with immutable, portable containers that run the same way on every machine. Below is a practical look at why you might move, what you gain, what you lose, and how to approach a migration without hassle.

The case for Docker

Comparison

Advantages

  1. “It works on my machine” becomes “it works everywhere”
    Package your app + runtime + dependencies into an image. No more “which PHP version is on production?” surprises.

  2. Fast, repeatable deployments
    Build once, run anywhere. Roll forward/back by swapping images. Blue-green or canary releases become straightforward.

  3. Dev–prod parity
    Compose files give you near-identical local and production stacks (web, queue, cache, DB). Fewer environment drift bugs.

  4. Isolation without full VMs
    Each service gets its own container and dependency tree. No global package collisions, less tinkering with system paths.

  5. Easy scaling
    Need more workers? Run more containers. Vertical or horizontal scaling is a knob, not a renovation.

  6. Cleaner CI/CD
    Pipelines push versioned images to a registry. Deployments reference tags, not bash scripts that “hope for the best.”

  7. Standardization across teams
    One way to run everything: docker build, docker run, docker compose up. Onboarding becomes “clone, up, done.”

  8. Safer changes
    Immutable images + healthchecks + resource limits (CPU/mem) reduce blast radius. If something goes sideways, swap the image.

  9. Microservices… if you need them
    You don't have to go microservices, but containers make service boundaries and independent lifecycles easier.

  10. Portability
    Run on a laptop, a VPS, bare metal, or any cloud. You're less tied to a single vendor's stack.

Dev To Prod

The trade-offs (yes, there are some)

issues

  1. Learning curve
    Images, layers, networks, volumes, registries — there's new mental model to learn. It's not hard, but it is different.

  2. Operational complexity
    Containers multiply (API, web, queue, scheduler, DB, cache…). You'll want guardrails: compose profiles, namespaces, naming conventions.

  3. Stateful services need care
    Databases and persistent storage require volumes, backups, and upgrade strategies. Don't casually rebuild your data layer.

  4. Networking & security footguns
    Extra hops (proxies, overlay networks) and new surfaces (daemon, registries). You must keep images updated and secrets out of images.

  5. Observability changes
    Logs go to stdout/stderr; metrics need sidecars/agents. Your monitoring and alerting stack should be container-aware.

  6. Orchestration can be overkill
    Kubernetes is powerful — and heavy. Many teams are perfectly fine with Docker Compose on a single VM, or a light orchestrator, for a long time.

  7. Performance caveats
    For most web apps the overhead is negligible. For very IO-heavy or low-latency workloads, test carefully (especially on macOS dev).

Is Docker worth it for you?

Give Docker a try if you recognize yourself here:

  • Multiple services (web, queue, scheduler, redis, etc.) or multiple apps.
  • Pain moving between dev/staging/prod or between teammates' machines.
  • You deploy weekly or daily and want rollbacks/zero-downtime.
  • You plan to scale out or change cloud providers.

Stick with traditional hosting (for now) if:

  • It's a single, simple site/app with rare updates.
  • Standard hosting already meets your needs (and budget).
  • You don't have time to learn the basics and the app is truly low-stakes.

A migration path

  1. Containerize locally
    Start with dev. Put your app and runtime in a Dockerfile. Use docker compose to define services (web, queue, cache, db).

  2. Make prod look like dev
    Keep the same services/versions. Replace local-only pieces (like bind mounts) with image builds + volumes where needed.

  3. Add CI/CD
    Have your pipeline build an image and push to a registry on every tagged release. Deploy by referencing that tag. CI/CD

  4. Start simple in production
    A single VM with Docker + Compose and a reverse proxy (Traefik or Nginx) is often enough. Add replicas for web/queue as needed. Production Scaling

  5. Handle state properly
    Use volumes for data, automated backups for DB, and test restores. Consider a managed DB if ops isn't your thing.

  6. Bake in the basics
    Healthchecks, environment variables/secrets, resource limits, structured logs, and a metrics endpoint. Small investments — big payoffs.

  7. Iterate
    When you need HA and auto-scaling, graduate to an orchestrator (Kubernetes, ECS, Nomad, or Swarm) with eyes open.

A basic example (docker-compose.yml for a PHP app)

app:
build:
  context: .
  dockerfile: Dockerfile
env_file: .env
depends_on:
  - db
  - redis
healthcheck:
  test: ["CMD", "php", "-v"]
  interval: 30s
  timeout: 5s
  retries: 3
 
web:
image: nginx:stable
volumes:
  - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
  - ./.storage/public:/var/www/public:ro
depends_on:
  - app
ports:
  - "80:80"
  - "443:443"
 
redis:
image: redis:7
command: ["redis-server", "--appendonly", "yes"]
volumes:
  - redis-data:/data
 
db:
image: mysql:8
environment:
  MYSQL_DATABASE: app
  MYSQL_USER: app
  MYSQL_PASSWORD: secret
  MYSQL_ROOT_PASSWORD: rootsecret
volumes:
  - db-data:/var/lib/mysql
 
volumes:
  db-data:
  redis-data:

This isn't production-ready on its own, but it shows the idea: immutable images, explicit dependencies, and clear, repeatable configuration.

Common issues (and quick fixes)

Issues

  • File permissions: run your app as a non-root user in the image; align UID/GID with mounted volumes.
  • Secrets: use environment variables or a secrets manager; never bake secrets into images.
  • TLS: terminate at your reverse proxy and automate certs (e.g., Let's Encrypt).
  • Backups: schedule DB dumps and snapshot volumes; test restores regularly.
  • Logs/metrics: send logs to stdout; ship with a collector. Expose Prometheus-friendly metrics or equivalent.
  • Image hygiene: pin versions, scan images, and update base images on a cadence.

Wrapping up

Docker brings consistency, speed, and portability to your deployments. You'll ship faster, roll back safely, and keep dev/prod in lockstep. In exchange, you'll take on new concepts and some operational discipline — worth it for most teams past “hello world,” but not mandatory for every project.

If you're curious, start small: containerize locally, deploy with Compose on a single VM, and grow from there. You can always keep it simple — or scale up when your app (and team) demand it. 🚀

Why move from “traditional hosting” to Docker (and when you shouldn't)