If you've ever heard "it works on my machine" or spent hours debugging environment differences between development and production, Docker for web applications solves that problem completely. Docker packages your app with its exact dependencies, configuration, and runtime into a portable container that runs identically everywhere — your laptop, a staging server, or production VPS.
This guide walks you through containerizing a real web application, from first Dockerfile to production deployment.
What Docker Actually Does
Docker creates isolated, lightweight environments called containers. Unlike virtual machines, containers share the host OS kernel, making them fast to start (seconds, not minutes) and efficient with resources.
Think of it this way: a VM is like renting an entire apartment building. A container is like renting just the apartment you need — same isolation, fraction of the overhead.
Without Docker: Your App → Different OS → Different PHP version → Different config → 🔥 Bugs With Docker: Your App → Same container everywhere → ✅ Works identically Installing Docker
Ubuntu/Debian
# Remove old versions sudo apt remove docker docker-engine docker.io containerd runc # Install Docker curl -fsSL https://get.docker.com | sudo sh # Add your user to the docker group (no sudo needed for docker commands) sudo usermod -aG docker $USER # Verify installation docker --version docker compose version macOS
Download Docker Desktop from docker.com — it includes everything you need.
Docker for Web Applications: Your First Dockerfile
A Dockerfile is a recipe that tells Docker how to build your container image. Here's a practical example for a Node.js application:
# Use official Node.js image as base FROM node:20-alpine # Set working directory inside the container WORKDIR /app # Copy dependency files first (better caching) COPY package*.json ./ # Install production dependencies RUN npm ci --only=production # Copy application code COPY . . # Expose the port your app listens on EXPOSE 3000 # Start the application CMD ["node", "server.js"] Build and run it:
# Build the image docker build -t myapp . # Run the container docker run -d -p 3000:3000 --name myapp myapp Your app is now running in a container, accessible at http://localhost:3000.
Dockerizing Common Web Stacks
PHP/Laravel Application
FROM php:8.3-fpm-alpine RUN docker-php-ext-install pdo pdo_mysql opcache WORKDIR /var/www/html COPY composer.json composer.lock ./ RUN composer install --no-dev --optimize-autoloader COPY . . RUN php artisan config:cache && \\ php artisan route:cache && \\ php artisan view:cache EXPOSE 9000 CMD ["php-fpm"] Python/Django Application
FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . RUN python manage.py collectstatic --noinput EXPOSE 8000 CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000"] WordPress with Custom Theme
FROM wordpress:6.7-php8.3-fpm COPY wp-content/themes/mytheme /var/www/html/wp-content/themes/mytheme COPY wp-content/plugins/myplugin /var/www/html/wp-content/plugins/myplugin Docker Compose: Multi-Container Applications
Most web applications need more than one container — your app, a database, maybe Redis for caching. Docker Compose orchestrates multiple containers together.
docker-compose.yml for a Typical Web App
version: '3.8' services: app: build: . ports: - "3000:3000" environment: - NODE_ENV=production - DATABASE_URL=postgres://user:pass@db:5432/myapp - REDIS_URL=redis://redis:6379 depends_on: - db - redis restart: unless-stopped db: image: postgres:16-alpine environment: - POSTGRES_DB=myapp - POSTGRES_USER=user - POSTGRES_PASSWORD=pass volumes: - pgdata:/var/lib/postgresql/data restart: unless-stopped redis: image: redis:7-alpine restart: unless-stopped nginx: image: nginx:alpine ports: - "80:80" - "443:443" volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf depends_on: - app restart: unless-stopped volumes: pgdata: Start everything with one command:
docker compose up -d All four services start together, connected on an internal Docker network where they can reach each other by service name (db, redis, app).
Dockerfile Best Practices
Use Multi-Stage Builds
Reduce your final image size by separating build and runtime stages:
# Stage 1: Build FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Stage 2: Production FROM node:20-alpine WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules EXPOSE 3000 CMD ["node", "dist/server.js"] This produces a much smaller image because build tools and dev dependencies stay in the builder stage.
Use .dockerignore
Just like .gitignore, a .dockerignore file prevents unnecessary files from being copied into your image:
node_modules .git .env *.md .github tests coverage Don't Run as Root
# Create a non-root user RUN addgroup -S appgroup && adduser -S appuser -G appgroup USER appuser Layer Caching
Docker caches each layer. Put things that change frequently (your code) after things that change rarely (dependencies):
# Dependencies change less often — cached COPY package*.json ./ RUN npm ci # Code changes often — rebuilds from here COPY . . Deploying Docker Containers to a VPS
Once your app is containerized, deploying to a VPS becomes straightforward. If you've been following our guide on staging environment setup, Docker makes staging even easier — identical containers across all environments.
Step 1: Push Your Image to a Registry
# Tag your image docker tag myapp registry.example.com/myapp:latest # Push to Docker Hub or a private registry docker push registry.example.com/myapp:latest Step 2: Pull and Run on Your VPS
# On your VPS docker pull registry.example.com/myapp:latest docker compose up -d Step 3: Set Up Automated Deployments
Combine Docker with CI/CD for push-to-deploy workflows:
# GitHub Actions example name: Deploy on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build and push Docker image run: | docker build -t myapp:latest . docker save myapp:latest | ssh user@vps 'docker load' - name: Restart on VPS run: ssh user@vps 'cd /app && docker compose up -d' Monitoring Docker Containers
Keep your containers healthy in production:
# Check running containers docker ps # View logs docker logs myapp --tail 100 -f # Resource usage docker stats # Restart a container docker compose restart app When your app needs to scale horizontally, Docker makes it simple — spin up more container instances behind a load balancer.
FAQ
Is Docker overkill for a simple website?
For a static site or basic WordPress blog, Docker adds unnecessary complexity. Docker shines when you need reproducible environments, run multiple services, or deploy across different servers consistently.
Does Docker replace my web hosting?
No. Docker runs on top of your hosting. You still need a VPS or server to run your containers. Docker simplifies what runs on that server and how you deploy updates.
How much memory does Docker use?
Docker itself uses minimal resources. Each container adds the memory your application actually needs — typically 50-200MB for a web app. A 2GB VPS can comfortably run 3-5 containers.
Can I use Docker with shared hosting?
No. Docker requires root access and a Linux kernel, which shared hosting doesn't provide. You need a VPS or dedicated server.
Containerize Your Stack with DeployBase
Docker for web applications eliminates deployment headaches by ensuring your app runs identically in development, staging, and production. Once containerized, you can deploy with confidence knowing there are no environment surprises.
At DeployBase, your application runs in a managed container on NVMe SSD storage, from $5.99/month with email support. DeployBase runs that container for you rather than giving you a Docker daemon or root on the host, so the build steps above belong on your own machine or in CI.
Get started with DeployBase → — deploy containers with confidence.