DEV Community

Charan Gutti
Charan Gutti

Posted on

🐳 Docker in Action: How I Containerized a Fullstack App Like a Pro (and You Can Too)

“Wait... you’re telling me I can run my React frontend, Node backend, and MongoDB — all together — with one command?”

Yep. That’s Docker magic in action.

In this post, you’re not just going to read about Docker.
You’re going to see it come alive — with a real-world fullstack setup: React + Node.js + MongoDB, running flawlessly in containers.

So grab your cup of coffee ☕ — this is going to be one of those “Oh, now I get Docker!” moments.


⚙️ What We’re Building

We’ll containerize this stack:

Frontend → React
Backend  → Node.js (Express)
Database → MongoDB
Enter fullscreen mode Exit fullscreen mode

And we’ll make them talk to each other — perfectly — using docker-compose.

When we’re done, you’ll run this entire setup with just:

docker-compose up
Enter fullscreen mode Exit fullscreen mode

That’s it. One command. Infinite power.


🧩 Step 1: The Folder Structure

Here’s our clean project layout:

fullstack-docker/
│
├── backend/
│   ├── server.js
│   ├── package.json
│   └── Dockerfile
│
├── frontend/
│   ├── src/
│   ├── package.json
│   └── Dockerfile
│
└── docker-compose.yml
Enter fullscreen mode Exit fullscreen mode

Simple, modular, and Docker-friendly.


🧠 Step 2: Let’s Build the Backend (Node.js)

Inside backend/server.js:

const express = require("express");
const mongoose = require("mongoose");
const app = express();

app.use(express.json());

mongoose.connect("mongodb://mongo:27017/dockerDemo", {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

app.get("/", (req, res) => {
  res.json({ message: "🚀 Hello from Node Backend via Docker!" });
});

const PORT = 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Enter fullscreen mode Exit fullscreen mode

backend/package.json:

{
  "name": "docker-backend",
  "version": "1.0.0",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^4.18.2",
    "mongoose": "^7.0.3"
  }
}
Enter fullscreen mode Exit fullscreen mode

🐳 Step 3: Backend Dockerfile

Create backend/Dockerfile:

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5000
CMD ["npm", "start"]
Enter fullscreen mode Exit fullscreen mode

This builds a lightweight Node image that runs your API inside a container.


⚡ Step 4: The React Frontend

frontend/package.json:

{
  "name": "docker-frontend",
  "version": "1.0.0",
  "private": true,
  "dependencies": {
    "react": "^18.3.0",
    "react-dom": "^18.3.0",
    "vite": "^5.0.0"
  },
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  }
}
Enter fullscreen mode Exit fullscreen mode

Add a basic frontend in frontend/src/App.jsx:

export default function App() {
  const [msg, setMsg] = useState("");

  useEffect(() => {
    fetch("http://localhost:5000/")
      .then(res => res.json())
      .then(data => setMsg(data.message));
  }, []);

  return (
    <div style={{ fontFamily: "sans-serif", textAlign: "center" }}>
      <h1>🐳 Dockerized Fullstack App</h1>
      <p>{msg}</p>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

🧱 Step 5: Frontend Dockerfile

Create frontend/Dockerfile:

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5173
CMD ["npm", "run", "dev"]
Enter fullscreen mode Exit fullscreen mode

⚙️ Step 6: The Docker Compose File (The Heartbeat ❤️)

Now create docker-compose.yml in the root:

version: "3"
services:
  frontend:
    build: ./frontend
    ports:
      - "3000:5173"
    depends_on:
      - backend

  backend:
    build: ./backend
    ports:
      - "5000:5000"
    depends_on:
      - mongo

  mongo:
    image: mongo
    restart: always
    ports:
      - "27017:27017"
    volumes:
      - mongo-data:/data/db

volumes:
  mongo-data:
Enter fullscreen mode Exit fullscreen mode

Boom.
You’ve just defined three containers — and how they interact — in under 25 lines.


🚀 Step 7: Run the Whole Stack

From your project root, run:

docker-compose up --build
Enter fullscreen mode Exit fullscreen mode

Watch the logs come alive.
Mongo starts first → then the backend → then the frontend.

Now visit:
👉 http://localhost:3000

You’ll see:

🐳 Dockerized Fullstack App
🚀 Hello from Node Backend via Docker!
Enter fullscreen mode Exit fullscreen mode

👏 You’ve just built a fullstack Docker setup.


🧩 How It All Works (Visually)

+---------------------------+
|         Frontend          |
|   React (Port 3000)       |
+-----------^---------------+
            |
            v
+---------------------------+
|         Backend           |
|   Node.js API (Port 5000) |
+-----------^---------------+
            |
            v
+---------------------------+
|          MongoDB          |
|   Database (Port 27017)   |
+---------------------------+
Enter fullscreen mode Exit fullscreen mode

All connected via Docker’s internal network — no manual config.


⚡ Why This Matters

Docker turns a 3-step setup nightmare into a single command.
You no longer need to:

  • Install Mongo locally
  • Run the backend separately
  • Keep terminals open for each service

Now it’s all portable, versioned, and repeatable.

Want to give your teammate this setup?
They just clone your repo and run:

docker-compose up
Enter fullscreen mode Exit fullscreen mode

…and it works. Every time.


💡 Tips to Level Up Your Docker Game

🪄 1. Use Named Volumes for Persistent DBs

Data survives even if you rebuild containers.

🪄 2. Add .dockerignore in both frontend & backend

Keeps builds fast by skipping unnecessary files:

node_modules
.git
.env
Enter fullscreen mode Exit fullscreen mode

🪄 3. Use docker-compose down -v to Reset Everything

Removes containers and volumes (fresh start for debugging).

🪄 4. Multi-Stage Builds for Production

For the frontend, build static files first and serve them with Nginx.

# Step 1: Build
FROM node:18-alpine as builder
WORKDIR /app
COPY . .
RUN npm install && npm run build

# Step 2: Serve
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
Enter fullscreen mode Exit fullscreen mode

This produces a smaller, faster production image.


🧠 Real-World Use Cases

Use Case Why Docker Helps
Hackathons Ship full apps instantly with one command.
Team Projects Eliminate “it doesn’t run on my system.”
Freelancing Deliver client projects as plug-and-play stacks.
Production Deployments Consistent across cloud platforms.

🔥 Pro Insight: The Developer’s Superpower

When you Dockerize your fullstack app, you’re not just coding — you’re building environments.
You gain control over every layer — from code to database to network.

That’s what separates good developers from great engineers.
You’re no longer bound to a system. Your environment is yours — defined, portable, and eternal.


🧭 Final Thoughts

Docker isn’t just a tool — it’s a developer’s safety net.
It lets you dream bigger without fearing deployment disasters.

So the next time you hear someone say:

“It works on my machine.”

You can smile, sip your coffee, and reply:

“Mine works everywhere. It’s Dockerized.” 🐳


🪄 Bonus Challenge for You

Try adding:

  • A Redis cache container
  • A simple Nginx reverse proxy
  • Environment variables with .env support

Then run docker-compose up again and watch your stack grow — effortlessly.

Top comments (0)