🐳Docker: isolate your projects
Every project in its own watertight bubble. Docker keeps your projects from stepping on each other, contains the damage when the agent experiments, and makes your deployments reproducible. For beginners and experts alike.
You’re going to run several projects on this machine. One wants Node 18, another Node 22. This one needs an old version of a library, that one the latest. Installed “loosely” on the system, they end up stepping on each other, and you hit the classic: “but it worked before.” Docker fixes this once and for all: every project lives in its own watertight bubble.
Why it changes your life (especially with an agent)
Three reasons, and the third is underrated:
- No more version conflicts. Each project carries its exact environment. Node 18 here, Node 22 there, no interference. You no longer install anything “globally” that risks breaking another project.
- Reproducible deployments. The container that runs on your machine is identical to the one that’ll run online. “It works on my machine” disappears: your machine is the container. It hugely simplifies going live (see Cloudflare Tunnel).
- The damage stays in the box. This is the key point for anyone working with an agent. An agent that experiments, a sketchy dependency, a script that goes off the rails: locked inside a container, they can’t wreck the whole machine. It’s the level of isolation above the “project folder”, a real sandbox per project (tie this back to Securing access).
Installing Docker
# The official method, in one command
curl -fsSL https://get.docker.com | sudo sh
# Allow your user to drive Docker without sudo (log out/in afterward)
sudo usermod -aG docker $USER
Check: docker run hello-world should download a tiny image and print a welcome message. If you see it, you’re ready.
Level 1, run something existing (beginner)
The best thing about Docker at the start is that you benefit from other people’s work. Tens of thousands of applications are already “boxed up,” ready to launch. Need a PostgreSQL database for a project? No laborious installation:
# Launch an isolated PostgreSQL database, in one line
docker run -d --name ma-db -e POSTGRES_PASSWORD=secret -p 5432:5432 postgres:16
docker ps # see what's running
docker logs ma-db # see the logs
docker stop ma-db # stop it (docker start ma-db to restart)
docker rm ma-db # delete the container (the box vanishes cleanly)
Level 2, a mini-project with several services (Docker Compose)
Most projects are several pieces: an app + a database, for example. Docker Compose describes all of that in a single compose.yml file, and launches the whole thing with one command.
# compose.yml, a small web app + its database, isolated together
services:
app:
build: .
ports:
- "8080:8080"
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
volumes:
- db-data:/var/lib/postgresql/data # the data survives restarts
volumes:
db-data:
docker compose up -d # everything starts, in the background
docker compose logs -f # follow what's happening
docker compose down # stop everything and clean up
Level 3, packaging YOUR project (the Dockerfile)
To box up your own code, you write a Dockerfile: the recipe that says how to build your project’s image.
# Dockerfile, your box's recipe
FROM node:22-slim # start from an official, lightweight Node image
WORKDIR /app
COPY package*.json ./
RUN npm ci # install the dependencies INSIDE the box
COPY . .
EXPOSE 8080
CMD ["node", "server.js"] # the command that starts the app
docker build -t mon-app . # build the image from the Dockerfile
docker run -d -p 8080:8080 mon-app
From now on, your project and its environment are one. You can run it on any machine that has Docker, identically.
Expert level, hardening the isolation
The container already isolates a lot, but by default it doesn’t stop where it could. When you host an exposed service, or you confine an agent, tighten the bolts:
- Don’t run as
rootinside the container : add an unprivileged user (USER node) in your Dockerfile. If someone escapes the app, they aren’t root. - Read-only filesystem :
docker run --read-onlystops the container from writing where it shouldn’t. - Limit resources :
--memory=512m --cpus=1stops a runaway container from choking the whole machine. - Reduce privileges :
--cap-drop=ALLremoves useless Linux capabilities; you only add back what you need. - Isolate the network : only publish (
-p) the ports that are actually needed; the rest stays invisible. And what you publish to the outside goes through Cloudflare Tunnel or stays on Tailscale, never in the clear on the Internet.