← Back to list

#4 of Learning DevOps Concepts as a Full Stack Developer: Writing First Dockerfile

Hey folks, it’s Amandeep back at it, still chasing my DevOps dreams as a full stack developer! It’s 07:51 PM IST on Tuesday, September 30…

Amandeep Singh · 2025-09-30 15:25 · 0 claps · 4.3 min read
#devops #dockerfiles #nodejs #typescript #full-stack
Open on Medium ↗
Wiki topics: EDU · Education & Learning 🌐 · Web Development ☁️ · DevOps & Cloud

#4 of Learning DevOps Concepts as a Full Stack Developer: Writing First Dockerfile

Photo by Joshua Reddekopp on Unsplash

Photo by Joshua Reddekopp on Unsplash

Hey folks, it’s Amandeep back at it, still chasing my DevOps dreams as a full stack developer! It’s 07:51 PM IST on Tuesday, September 30, 2025, and I just wrapped up writing my first Dockerfile. This is my fourth article of the journey and I’m spilling the beans on what a Dockerfile is, breaking down its structure in my own words, sharing a Node.js TypeScript backend example I pieced together (including the folder setup I used), and walking you through how I got it running. I’m still learning on the fly here, so join me for the ride — maybe we’ll learn something together!

1. What is a Dockerfile?

Okay, so a Dockerfile? Basically its a set of commands that tells Docker how to whip up a container. It’s just a plain text file where I wrote down what I need — starting with a base image, tossing in my app’s code and dependencies, and laying out the steps to make it work. I gave it a shot today, and it felt like handing Docker a to-do list — kinda cool!

2. Getting the Hang of Dockerfile Structure

Dockerfiles aren’t too tricky once you get the flow, and here’s how I’ve figured it out so far:

  • The “FROM” Kickoff: Mostly Every Dockerfile starts with a FROM line, which is like picking the base flavor—say, FROM node:18 to grab the Node.js 18 image. It’s where I start building everything.
  • The Commands Part: Next, I throw in commands to set things up — stuff like installing dependencies, copying files, and tweaking the setup. Think RUN, COPY, and WORKDIR—I’ll show these in action soon.
  • Keeping It Going: Then I add more steps — like opening ports, deciding how to start the app, or setting env variables. It’s like layering ingredients until my “dish” is ready to serve.

It hit me that it’s just a step-by-step guide Docker follows — nothing fancy, just my instructions in order!

# FROM section 
Usually here we write a base image, which means what is the minimum we need
over which we are builting our app. For example we need a Node js to run a node application.

# Commands section (Packaging commands)
Here we write commands to Built the image, what are neccessary steps required
to run our project.

# final wrap up command
Usually this command is the final one which is used to execute the container.

3. Example: Node.js Backend with TypeScript

Let’s get real! I threw together a simple Node.js backend with TypeScript and here’s the Dockerfile I hacked out. It’s a basic API, and this is how I boxed it up:

FROM node:18-alpine. # The from part
# set of packaging commands 
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build || npx tsc
EXPOSE 3000
# command to execute the container
CMD ["node", "dist/index.js"]

Folder Structure of My Node Project

Here’s how I organized my project folder before building the Dockerfile — kept it simple but functional:

my-node-app/
├── src/
│   └── index.ts          # Main TypeScript file with the API logic
├── dist/                 # Compiled JavaScript goes here (created after build)
├── node_modules/         # Dependencies installed by npm
├── package.json          # Lists dependencies and scripts
├── package-lock.json     # Locks dependency versions
└── tsconfig.json         # TypeScript config file
└── Dockerfile            # The recipe file itself
  • src/index.ts has my API code (e.g., app.get('/hello', (req, res) => res.send('Hi from Docker!'))).
  • package.json includes "scripts": { "build": "tsc" } and dependencies like "express" and "typescript".
  • tsconfig.json sets up TypeScript (I used a basic setup with "outDir": "dist").
  • The dist folder pops up after npm run build compiles the code.

4. How I Got My Dockerfile Running

This was the fun part where I actually saw it come to life! Here’s what I did:

  • Build the Image: I popped into my terminal, went to the my-node-app folder, and typed:
docker build -t my-node-app:1.0 .
  • The -t my-node-app:1.0 gives it a name, and the dot (.) points to my current spot. you can also use tags to gave versioning to our images. We will explore this in future articles.
  • Run the Container: Once it built, I fired it up with:
docker run -p 3000:3000 -d my-node-app:1.0
  • The -p 3000:3000 links my machine’s port to the container’s, and -d keeps it running in the background. To check whether containers starts or not you can usedocker ps. it worked in my case. we can also execute it using some cli arguments, we will explore that in future.
  • Test It Out: I typed http://localhost:3000/hello in my browser and got “Hi from Docker!”
  • Clean Up: After playing around, I stopped it with docker stop <container_id> and deleted it with docker rm <container_id> to keep things clean.

5. Making Sure Docker Images Run Everywhere

I am doing experiments in both apple silicon chips and intel chips and I created a Docker image on my Intel-chip Mac, pushed it to Docker Hub, and tried running it on a machine with a Silicon (Apple M1) chip. It wouldn’t work — total frustration! The issue? The image was built for an Intel architecture, but the Silicon chip needed an ARM-based one. Here’s how I’m tackling this now:

  • Use Multi-Architecture Images: I learned to build images for multiple platforms using docker buildx. I ran:
docker buildx build --platform linux/amd64,linux/arm64 -t my-node-app:1.0 --push .
  • This creates an image that works on both Intel and ARM chips. The — push sends it to Docker Hub.
  • Check the Base Image: Starting with a multi-arch base like node:18 helps, as it supports various architectures.
  • Test Across Machines: I now test my image on different setups (Intel and Silicon) to catch issues early. Pulled it on a friend’s M1 Mac, and it ran like a charm this time!

This step ensures my app isn’t stuck on one machine type — huge relief!

What’s Next?

So overall it was fun and great learning, now as an engineers we first build and there is always a room for improvements/optimizations later, so I will probably spent some more time in exploring best practices and otpmization techniques while writing the docker file and I will share with you in upcoming articles. Got any Dockerfile hacks or silly mistakes you’ve made? Drop them in the comments — I’d love to hear your stories!


메타데이터
post_id
7dfc77361d62
slug
4-of-learning-devops-concepts-as-a-full-stack-developer-writing-first-dockerfile-7dfc77361d62
url
https://medium.com/@amanbnl/4-of-learning-devops-concepts-as-a-full-stack-developer-writing-first-dockerfile-7dfc77361d62
canonical_url
https://medium.com/@amanbnl/4-of-learning-devops-concepts-as-a-full-stack-developer-writing-first-dockerfile-7dfc77361d62
author_url
https://medium.com/@amanbnl
status
ok
fetched_at
2026-07-17 03:39:40