← BlogFrom One Server to Multi-Service: Splitting with Docker Compose

From One Server to Multi-Service: Splitting with Docker Compose

June 10, 2026

This site started as a single Next.js app. It was fast, but I wanted to show my Docker and service-architecture skills on my CV. So I split the project into three independent services.

Why split?

A monolith is practical at first, but as it grows three problems appear:

  • Frontend and backend run in the same process; if one crashes, the other suffers.
  • The database risks being exposed to the outside world.
  • Dependencies (Node, Python, Postgres) are crammed into one image.

Three services

The new layout has clear responsibilities:

layered deployment of the front, api and db services with Docker Compose
  • front — Next.js (App Router). The only exposed service; it handles the request.
  • api — FastAPI + SQLAlchemy. On the internal network only; not directly reachable.
  • db — PostgreSQL. Talks only to the API, closed to the outside.

One command to rule them all

docker-compose.yml wires everything together. Thanks to a health check, the API waits until the database is ready:

services:
  db:
    image: postgres:16-alpine
    networks: [internal]
  api:
    build: ./api
    depends_on:
      db: { condition: service_healthy }
    networks: [internal]
  front:
    build: ./front
    depends_on: [api]
    networks: [internal, traefik_default]

Now docker compose up is all it takes. The three services come up in order, find each other on the internal network, and only the frontend is exposed.

Isolated services aren't just safer; they're easier to maintain because you can restart and scale each one independently.

Result

Splitting added some code but clarified the architecture. The database is now fully closed off, the API lives on a private network, and the frontend is the single entry point. In the next post I'll explain how the frontend and API talk to each other securely.