As my projects grew, I started using compiled languages—specifically TypeScript. TypeScript is fantastic for coding, but it introduces a problem when deploying containers. To run a TypeScript app, you first need to compile it into regular JavaScript using the TypeScript Compiler (tsc).
In my previous post, I successfully got a multi-container database and backend system running. But to make it work, I had to manage a collection of bash scripts: setup.sh to create networks and volumes, start-db.sh for MongoDB, start-backend.sh for the server, and a buggy cleanup.sh to tear things down.
Up until now, I have only run single containers in isolation. But real apps are rarely that simple. A standard web application has a frontend, a backend API, and a database like PostgreSQL or MongoDB.
When containerizing frontend applications like React, Vue, or Angular, you hit a structural fork in the road. In development, you want a Node.js server running in the background, watch-compiling code changes and hot-reloading your browser instantly. But in production, you don’t need Node.js at all! You just need to serve static HTML, CSS, and JS bundles as fast as possible.
One of the most frustrating things when containerizing applications is waiting for build steps to finish. If your Dockerfile is not structured correctly, a tiny one-character text change in your source code can force Docker to download all your project dependencies from scratch. In this post, I will write about how Docker determines cache invalidation, detail a smart structure for caching…
One of the core rules of building modern software (according to the famous Twelve-Factor App methodology) is that configuration must be strictly separated from code. Hardcoding database URLs, API ports, or credentials directly into your codebase is a recipe for disaster. In this post, I will explore how Docker solves this problem using Environment Variables, how to set defaults in a Dockerfile,…
When I first started running docker build ., I noticed a line in my terminal output that read something like: => [internal] load build context => => transferring context: 10.49MB
As I started writing more advanced Dockerfiles, I noticed two instructions that seemed to do almost the exact same thing: CMD and ENTRYPOINT. Both tell the container what program to run when it starts up. But they behave very differently when you start passing arguments. In this post, I will explore the differences, document my experiments with the project files, and show how combining both lets…
Now that I know how to run a basic container, I wanted to build something more practical: a containerized web server that serves a custom webpage. In this post, I write about configuring Nginx, understand the difference between RUN and COPY instructions, and look at how Docker’s layer caching system works—including a common “caching trap” that can break builds.
When I first heard about Docker, it sounded like magic. Everyone was talking about “containers,” “images,” and “isolation.” But what do these terms actually mean from first principles? In this first post of my Docker learning journey, I want to strip away the jargon and document how I got my very first container up and running.