Skip to main content

Every developer knows Docker. Spin up a container locally, run your database in isolation, share a docker-compose file with the team. But when it comes to production, things get murkier. Kubernetes feels like overkill for a five-person team. Raw Docker on a VPS feels too bare. So what actually works?

After years of deploying containerised applications for clients ranging from bootstrapped startups to mid-sized SaaS platforms, we have strong opinions on this. Here is what we have learned.

TL;DR

  • Docker in production is not the same as Docker in development. Treat them differently.
  • For teams under 20 developers, Docker Compose or Docker Swarm often beats Kubernetes on cost, complexity, and time-to-deploy.
  • Multi-stage builds, health checks, and proper logging are non-negotiable for production containers.
  • Security matters more in production: run as non-root, scan images, limit capabilities.
  • Monitoring and rollback strategies are where most small teams fall short.

The “Do I Even Need This?” Question

Before diving in, ask yourself honestly: does your project need containerisation in production? If you are running a single Node.js application on a single server, a well-configured PM2 setup with systemd might serve you perfectly well. Containers add a layer of abstraction, and abstraction has costs.

Containers make sense when you need:

  • Reproducible environments across development, staging, and production
  • Multiple services running on the same host without dependency conflicts
  • Easy horizontal scaling when traffic patterns are unpredictable
  • Consistent deployments across different infrastructure providers

If none of these apply, you might be adding complexity for complexity’s sake. And that is fine to admit.

Skip Kubernetes (Probably)

This is our most controversial take, and we stand by it. Kubernetes is brilliant. It is also wildly over-prescribed for small teams.

The operational overhead of maintaining a Kubernetes cluster, even a managed one on GKE, EKS, or AKS, is significant. You need to understand networking policies, ingress controllers, persistent volume claims, RBAC, and a dozen other concepts before you deploy your first pod. For a team of three to ten developers shipping a SaaS product, that learning curve eats into your actual product development time.

Docker Compose handles most small-team production needs surprisingly well. Define your services, networks, and volumes in a single file. Deploy with a single command. It is boring, predictable, and that is exactly what you want in production.

Docker Swarm sits in the middle ground. If you need multi-node orchestration, rolling updates, and service discovery without the Kubernetes learning cliff, Swarm delivers. Yes, Docker Inc. has been quiet about Swarm’s future, but the technology is stable and battle-tested. For teams that need more than Compose but less than Kubernetes, it remains a solid choice.

Production-Ready Dockerfiles

Your development Dockerfile and your production Dockerfile should not be the same file. Here is what a production-ready Dockerfile looks like for a typical Node.js application:

# Build stage
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

# Production stage
FROM node:22-alpine
RUN addgroup -g 1001 -S appgroup && adduser -S appuser -u 1001 -G appgroup
WORKDIR /app
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/package.json ./
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/index.js"]

Key principles at play here:

  • Multi-stage builds keep your final image small by excluding build tools and dev dependencies
  • Non-root user limits the blast radius if your container is compromised
  • Health checks let your orchestrator know when something has gone wrong
  • Alpine base reduces attack surface and image size
  • npm ci ensures deterministic installs from your lockfile

Secrets Management

Never bake secrets into your Docker images. This sounds obvious, but we still see it regularly in client codebases. Environment variables at runtime are the minimum. Docker Swarm has built-in secrets management. For Compose-based setups, tools like docker-compose with .env files work, but consider a proper secrets manager like HashiCorp Vault or even Bitwarden’s Secrets Manager for anything beyond a handful of variables.

The rule is simple: if someone pulls your Docker image from a registry, they should find zero credentials inside it.

Logging and Monitoring

This is where most small teams drop the ball. Your containers are running, deployments are smooth, and then something breaks at 3am and you have no idea what happened because your logs vanished when the container restarted.

At minimum, configure:

  • A logging driver that persists logs outside the container (json-file with rotation, or better yet, ship to a centralised system)
  • Structured logging in your application (JSON format, not free-text)
  • Container metrics via cAdvisor, Prometheus, or a managed monitoring service
  • Alerting on container restarts, health check failures, and resource exhaustion

Grafana Cloud’s free tier handles monitoring for most small deployments. Pair it with Loki for logs and you have a solid observability stack without the operational burden of self-hosting.

Deployment Strategies That Actually Work

For small teams, keep it simple:

Blue-green deployments: Run two identical environments. Deploy to the inactive one, verify it works, then switch traffic. Docker Compose makes this straightforward with separate compose files or service naming conventions.

Rolling updates: If using Swarm, this is built in. Configure update_config with appropriate parallelism and delay settings. Always set rollback_config so failed updates automatically revert.

The pragmatic approach: For many small projects, a simple script that pulls the latest image, stops the old container, and starts the new one with a health check gate is perfectly adequate. Not every deployment needs to be zero-downtime. If your users can tolerate 30 seconds of maintenance, do not over-engineer it.

Image Security

Container security is not optional in 2026. The EU Cyber Resilience Act is tightening requirements, and supply chain attacks targeting container images are increasingly common.

Practical steps:

  • Scan your images with Trivy or Snyk before every deployment
  • Pin your base image versions instead of using :latest
  • Use distroless or Alpine images to minimise the attack surface
  • Enable Docker Content Trust to verify image signatures
  • Limit container capabilities with --cap-drop ALL and only add back what you need

A five-minute Trivy scan in your CI pipeline catches most vulnerabilities before they reach production. There is no excuse for skipping it.

When to Graduate to Kubernetes

There comes a point where Docker Compose and Swarm are not enough. Signs you might be ready for Kubernetes:

  • You are managing more than 15-20 services across multiple nodes
  • You need sophisticated autoscaling based on custom metrics
  • Your team has grown to the point where self-service deployments with RBAC are necessary
  • You need multi-region deployments with service mesh capabilities

If you are not there yet, do not rush it. Premature Kubernetes adoption is one of the most expensive mistakes a small team can make, both in direct costs and in developer productivity lost to cluster maintenance.

The Bottom Line

Docker in production does not have to be complicated. Start with Compose. Write proper Dockerfiles. Set up logging and monitoring from day one. Scan your images. Have a rollback plan.

The teams that succeed with containers are not the ones running the most sophisticated orchestration. They are the ones who matched their tooling to their actual needs.

At REPTILEHAUS, we help teams set up production container workflows that are right-sized for their stage. Whether you are containerising your first application or migrating from a tangled Docker Compose setup to something more robust, get in touch. We have done this a few times.

📷 Photo by Rubaitul Azad on Unsplash