Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
DevOpsJune 20, 20265 min read

Docker for app developers: A mental model that sticks

Master Docker for app developers by shifting your mental model. Learn how containers actually work, why they aren't VMs, and how to build efficient images.

DockerDevOpsContainersSoftware DevelopmentLinuxCI/CD
Shipping containers and cranes at Hamburg port showcasing global trade.

Stop thinking of Docker as a lightweight virtual machine. That's the most common trap I see developers fall into, and it's exactly what leads to bloated images, security nightmares, and "it works on my machine" syndrome.

When you treat a container like a VM, you're trying to shove an entire OS into a box. Instead, you need to view a container as a single process—or a tightly coupled set of processes—that shares the host kernel but lives in its own isolated namespace. Once you get this shift, the tooling stops feeling like magic and starts feeling like a predictable utility.

Rethinking Docker for app developers

The biggest hurdle for most of us is the transition from "installing software on a server" to "declaring a state for an application." When I first started with Docker back in 2017, I spent three days trying to get a systemd service to run inside a container. I was fighting the tool because I was trying to force a server-side architecture into a containerized workflow.

The breakthrough came when I stopped trying to manage the OS and started managing the application dependencies. Here is how I look at the stack now:

  1. The Base Image: Think of this as your language runtime and the minimum set of OS utilities required to execute your code.
  2. The Layers: Each RUN or COPY command in your Dockerfile creates a layer. If you change a line at the top, everything below it must be rebuilt.
  3. The Entrypoint: This is the process that defines the life of the container. If it dies, the container dies.

Why layers matter for performance

If you’re writing a Dockerfile for a Node.js app, don't just COPY . . and then RUN npm install. If you do that, every tiny change to a CSS file or a log message triggers a full re-install of your node_modules because the cache layer is invalidated.

Instead, structure it like this:

Dockerfile
# Use a specific version, not 'latest'
FROM node:20.11.0-slim

WORKDIR /app

# Copy dependency files first
COPY package*.json ./
RUN npm ci --only=production

# Now copy the rest of your source code
COPY . .

CMD ["node", "server.js"]

By copying package.json first, Docker caches the npm ci step. Now, when you change your application code, the build process skips the heavy lifting of downloading dependencies. This simple change usually shaves about 20-30 seconds off a build time.

Beyond the local environment

Once you've got your local build optimized, you'll eventually need to ship it. If you're managing complex deployments, GitLab CI and Docker: Secure Microservices CI/CD is a great reference for ensuring your container registry isn't a security hole.

However, remember that Docker is just the packaging format. The real power lies in how you handle state. If you find yourself trying to run a database inside a container during production, stop. Keep your databases external—whether that's a managed RDS instance or a persistent volume setup if you're venturing into WordPress Kubernetes Multisite: Solving Storage and Database Persistence. Containers are ephemeral by design; your data should not be.

Common pitfalls to watch for

A surprised woman with yellow nails holds a small red alarm clock against a white backdrop.

I’ve seen plenty of projects crash because of these three oversights:

  • Running as root: By default, Docker runs as root. This is a massive security risk. Always add a user in your Dockerfile and switch to it before the CMD instruction.
  • Ignoring .dockerignore: If you don't use a .dockerignore file, you're likely sending your local .git folder, your node_modules folder, and your .env files into the image context. Keep your images lean and clean.
  • Hardcoding configurations: Use environment variables. If you’re baking secrets or environment-specific URLs into the image, you’ve broken the portability that makes Docker useful in the first place.

FAQ: Docker for app developers

Q: Should I use Alpine or Debian-slim for my base images? A: It depends, but usually slim is safer. Alpine uses musl libc instead of glibc, which can cause weird, hard-to-debug crashes with some C-based extensions. If you don't have a strict size requirement, slim is almost always the more stable choice.

Q: How do I know if my container is "too big"? A: Use docker history <image_name> to see which layers are consuming the most space. If you see a layer that's 500MB, you probably forgot to clean up your build artifacts or cache files in the same RUN command where you created them.

Q: Is it "cheating" to use Docker Compose in production? A: Not at all. For single-server deployments or small stacks, docker-compose is perfectly adequate. Don't feel pressured to jump to Kubernetes unless you actually have the complexity that requires it.

Final thoughts

Colorful confetti scattered over the word 'Finally' symbolizing celebration or achievement.

Docker for app developers isn't about memorizing flags or learning every command in the CLI. It's about respecting the boundaries of the container. Keep your processes focused, your layers thin, and your state externalized.

I’m still experimenting with multi-stage builds to see how much smaller I can get our internal tool images, and I’m honestly not sure if the complexity of some advanced build patterns is worth the trade-off in readability. Start simple, keep your images predictable, and don't be afraid to delete a Dockerfile and start over if it feels like you're fighting the tool.

Back to Blog

Similar Posts

A classic MS-DOS terminal screen displayed on a laptop keyboard with vivid illumination.
DevOpsJune 20, 20264 min read

Nginx as a reverse proxy: The config explained line by line

Nginx as a reverse proxy is the backbone of reliable web traffic routing. Learn how to configure your setup line-by-line for production-grade performance.

Read more
Close-up of colorful programming code on a computer screen, showcasing digital technology.
DevOps
June 20, 2026
4 min read

Zero-downtime deploy with GitHub Actions: A Practical Guide

Achieve a zero-downtime deploy with GitHub Actions using blue-green strategies. Learn how to keep your services running seamlessly during every release.

Read more
Deliveryman in red uniform delivers package outdoors in daylight.
DevOpsJune 20, 20264 min read

Running background workers with systemd for production reliability

Running background workers with systemd is the gold standard for process management. Learn to write robust service files to keep your tasks alive.

Read more