Docker Compose in Production: A Defensible Single-Host Pattern

Current Ray demonstrates keyboard focus, labelled controls, captions and responsive zoom as parts of an inclusive website experience.

Reviewed: 29 August 2026 · Next review: 28 February 2027
Author: Ozlin Info Editorial Team · Human review: Lin

Docker Compose can be a practical production option for a modest application on one well-managed server. It provides a declarative description of services, networks, volumes and runtime configuration, and Docker documents a production workflow using a base Compose file with a production-specific override (Docker Docs — Use Compose in production).

That does not make one Compose host highly available. The host, storage and Docker daemon can remain single points of failure. If the service requires automatic rescheduling across machines, multi-node availability or sophisticated traffic management, evaluate an orchestrator or managed platform instead of implying that a restart policy solves infrastructure failure.

Define the operating target first

Record the service-level needs before writing YAML:

  • expected traffic and resource profile;
  • tolerable downtime and data loss;
  • backup and restore objectives;
  • public and private network paths;
  • data classification and secrets;
  • patch, release and rollback ownership;
  • monitoring and alert response; and
  • conditions that require migration beyond one host.

For a small content site, several minutes of controlled recovery may be acceptable. A payment or safety-critical service may need a very different architecture. Compose is a packaging and lifecycle tool, not a substitute for risk assessment.

Build immutable, reviewable images

Production application code should normally be inside a versioned image rather than bind-mounted from a mutable source directory. Use a multi-stage Dockerfile so compilers, package managers and build-time credentials stay out of the final runtime image. Choose a small, trusted base image, install only required packages and run as a non-root user where the application permits it.

Docker notes that tags are mutable. Pinning an image digest improves reproducibility, but it also means the team must deliberately update the digest to receive fixes. Automate notifications or pull requests rather than pinning and forgetting (Docker Docs — Building best practices).

A defensible release records:

  • source commit and build workflow;
  • image repository, tag and digest;
  • software bill of materials or dependency inventory where appropriate;
  • vulnerability-review result and accepted exceptions;
  • configuration version and database migration; and
  • deployment and rollback evidence.

Do not bake passwords, API keys or private certificates into an image layer. Removing a secret in a later layer does not reliably erase it from earlier image history.

Separate development and production configuration

Keep common service definitions in compose.yaml and apply production differences through a reviewed override such as compose.production.yaml:

services:
  web:
    image: registry.example.test/acme-web@sha256:REPLACE_WITH_APPROVED_DIGEST
    restart: unless-stopped
    read_only: true
    tmpfs:
      - /tmp
    ports:
      - "127.0.0.1:8080:8080"
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/healthz"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 20s
    security_opt:
      - no-new-privileges:true
    networks:
      - edge
      - app

  database:
    image: mysql@sha256:REPLACE_WITH_APPROVED_DIGEST
    restart: unless-stopped
    volumes:
      - db-data:/var/lib/mysql
    networks:
      - app

networks:
  edge: {}
  app:
    internal: true

volumes:
  db-data: {}

This is an illustration, not a drop-in deployment. The image may not support a read-only filesystem, wget, the shown port or non-root operation. Validate the actual image and application.

Render the combined configuration before deployment:

docker compose -f compose.yaml -f compose.production.yaml config

Review the output for unintended port exposure, missing variables, duplicate mounts and development flags.

Expose the minimum network surface

Publish only ports that genuinely need host access. If a reverse proxy on the same host is the only caller, bind the application to loopback rather than every interface. Keep databases and queues on internal networks without published host ports unless an authorised operational requirement says otherwise.

Network separation reduces accidental reachability but is not an authorisation system. The application must still authenticate callers, authorise actions, validate input and protect transport where traffic crosses an untrusted boundary.

Avoid privileged containers, host network mode, broad Linux capabilities and Docker socket mounts unless a reviewed use case requires them. Access to the Docker socket is effectively high-impact control of the host.

Handle secrets according to the deployment model

Compose can mount declared secrets into a service as files, which is generally preferable to copying them into the image or printing them in environment dumps. On standalone Compose, however, the source secret is still a host file or external resource that the operator must protect. Docker Swarm secrets add encrypted transport and at-rest handling for Swarm services; those guarantees do not automatically apply to every standalone Compose deployment (Docker Docs — Manage sensitive data with Docker secrets).

Use restrictive host permissions or an appropriate secret manager, grant each service only the secrets it needs, avoid logging values and define rotation. Treat .env as configuration convenience, not an encrypted vault, and keep secret-bearing files out of source control and build context.

Use health checks without confusing them with monitoring

A health check should test whether the service can perform a small, representative local function. Compose can wait for a dependency marked service_healthy before creating a dependent service (Docker Docs — Control startup order). This helps startup sequencing, but it does not guarantee that a remote user can reach the application or that every dependency works.

Combine container health with external availability checks, application metrics, structured logs and host monitoring. Track disk, memory, CPU, file descriptors, container restarts, certificate expiry, backup results and application-specific failures. Put retention and access controls around logs because they may contain personal or security-relevant information.

Set memory and CPU expectations carefully. A hard limit can contain one service but can also cause abrupt failure under legitimate load. Observe real usage, preserve host capacity and test behaviour when a limit is reached.

Separate persistent data from disposable containers

Containers should be replaceable. Store mutable application data in named volumes, bind mounts with explicit ownership or external services. Document exactly what must be backed up: database-consistent data, uploads, configuration, certificates, encryption keys and any queue or object storage required for recovery.

A copy of a live database directory is not automatically a valid backup. Use the database's supported logical or physical backup method, protect the result and test restoration into an isolated environment. Record recovery time and recovered data point rather than merely checking that a backup file exists.

Named volumes do not create backups, replication or geographic resilience. They only separate data lifecycle from a particular container.

Release one controlled change at a time

A single-host deployment can use this sequence:

  1. build and test the image in CI;
  2. approve the image digest and configuration;
  3. take or verify the required backup;
  4. pull images without replacing running containers;
  5. run backward-compatible database migrations where possible;
  6. recreate the affected service;
  7. verify health, external behaviour, logs and data; and
  8. retain a tested rollback route.

Docker's production guide shows rebuilding and recreating one service with docker compose build web followed by docker compose up --no-deps -d web. When deploying registry images, use the equivalent controlled pull and up flow for the exact approved reference. Understand that recreating a container can cause brief downtime on one host.

Rollback must account for data schema. Restoring an earlier application image may fail after an irreversible migration. Prefer expand-and-contract migrations, backups and explicit compatibility windows.

Patch the complete stack

Rebuild images regularly with updated base images and application dependencies, then test and deploy them. Also patch the host kernel, Docker Engine, Compose plugin and reverse proxy. Schedule reboot paths and verify containers return as expected.

Review configuration drift with docker compose config, image digests and host records. Do not use latest as an undocumented release decision, and do not enable unattended replacement of stateful services without tested compatibility and rollback.

For help designing or reviewing a scoped web hosting deployment, see Ozlin Info's web development services or contact Ozlin Info.

Related reading: Incident response and disaster recovery: design for evidence and recovery.


General-information disclaimer

This article provides general technical information only. It is not a complete architecture, security assessment, availability commitment or backup design. Production controls must reflect the actual application, images, host, data and recovery requirements.

AI-assistance disclosure

AI tools assisted with source discovery, outlining and copyediting. A human reviewer must validate every command, image capability, deployment assumption, service claim and publication decision before release. No uptime, security or recovery outcome is guaranteed.

Primary sources checked

Source access date: 29 August 2026.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *