I used to deploy my apps with scp and manual config files. Then I discovered Docker and realized I'd been wasting hours every week.
Here's the practical Docker guide I wish I had when starting out.
Why Docker Changes Everything
Before Docker, deploying meant:
- "Works on my machine" disasters
- Manual dependency installation on every server
- Environment differences between dev, staging, and production
- Hours debugging "but it worked locally!"
With Docker, you ship a container — a box with your app AND everything it needs. Same box everywhere.
Docker in 5 Minutes
Installation
# macOS / Windows: Install Docker Desktop
# Linux:
curl -fsSL https://get.docker.com | sh
Your First Dockerfile
# Start from an official base image
FROM node:20-alpine
# Set working directory
WORKDIR /app
# Copy dependency files first (for caching)
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy source code
COPY . .
# Expose port
EXPOSE 3000
# Start the app
CMD ["node", "server.js"]
Build and run:
docker build -t my-app .
docker run -p 3000:3000 my-app
That's it. Your app runs in a container.
The Commands You'll Use 90% of the Time
# Build an image
docker build -t app-name .
# Run a container
docker run -p 8080:3000 app-name
# Run in background (detached)
docker run -d -p 8080:3000 app-name
# List running containers
docker ps
# Stop a container
docker stop container-id
# View logs
docker logs container-id -f
# Shell into a running container
docker exec -it container-id sh
# Clean up everything
docker system prune -a
Docker Compose: Run Multiple Services
Most real apps need a database, cache, and the app itself. Docker Compose orchestrates all of them.
# docker-compose.yml
version: "3.9"
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
depends_on:
- db
- redis
db:
image: postgres:16
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
postgres_data:
Start everything:
docker compose up -d
One command. App + database + cache, all running.
Real-World Tips for Solo Devs
1. Use .dockerignore
node_modules
.git
.env
*.log
dist
Keeps your images small and build times fast.
2. Multi-stage Builds (Smaller Images)
# Stage 1: Build
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Production (much smaller)
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/server.js"]
Result: 1.2GB → 180MB image.
3. Never Store Secrets in Images
# Wrong
ENV DATABASE_PASSWORD=supersecret
# Right: use environment variables at runtime
docker run -e DATABASE_PASSWORD=$DB_PASS my-app
4. Health Checks
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
Docker restarts unhealthy containers automatically.
Free Deployment Options in 2026
With Docker, you can deploy anywhere for free or cheap:
- Railway.app — push Dockerfile, it deploys (free tier available)
- Render.com — Docker support, free tier
- Fly.io — excellent free tier, global edge
- GitHub Actions + VPS — $5/month VPS, full control
The Docker Mindset
Think of containers as immutable units:
- Never SSH into a production container to make changes
- Fix → rebuild → redeploy
- Logs go to stdout/stderr, not files
- State goes in volumes or external databases
Bottom Line
Docker takes a few days to click. Once it does, you'll never go back to manual deployments.
Start with a simple Dockerfile for your next project. Then add Docker Compose when you need a database. You'll wonder how you shipped apps without it.
Building something solo? The Freelancer OS Notion Template at guittet.gumroad.com helps you manage your projects, clients, and finances in one place (€19). Check it out. 🚀
Top comments (0)