Docker Web Application Containerization Setup
Docker packages an application with all its dependencies into an isolated container. Identical behavior across dev, staging, and production. Simplifies deployment and horizontal scaling.
Multi-stage Dockerfile for PHP/Laravel
# Dockerfile
# Stage 1: Install PHP dependencies
FROM composer:2.7 AS composer
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-scripts --no-interaction
# Stage 2: Build frontend
FROM node:20-alpine AS node
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY resources/js ./resources/js
COPY resources/css ./resources/css
COPY vite.config.ts tsconfig.json ./
RUN npm run build
# Stage 3: Final image
FROM php:8.3-fpm-alpine
RUN apk add --no-cache \
nginx \
supervisor \
postgresql-libs \
&& docker-php-ext-install pdo_pgsql opcache
WORKDIR /var/www/html
# Copy from previous stages
COPY --from=composer /app/vendor ./vendor
COPY --from=node /app/public/build ./public/build
COPY . .
# OPcache configuration
RUN echo "opcache.enable=1\nopcache.memory_consumption=256\nopcache.validate_timestamps=0" \
>> /usr/local/etc/php/conf.d/opcache.ini
# Run via supervisor (nginx + php-fpm)
COPY docker/supervisord.conf /etc/supervisord.conf
COPY docker/nginx.conf /etc/nginx/nginx.conf
RUN chown -R www-data:www-data storage bootstrap/cache
EXPOSE 80
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
Multi-stage Dockerfile for Node.js
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/public ./public
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]
.dockerignore
node_modules
.git
.env
*.log
/tests
/coverage
/.github
docker-compose*.yml
Dockerfile*
Image size optimization
# Use alpine images
FROM php:8.3-fpm-alpine # ~80 MB vs 400 MB for debian-based
# Combine RUN commands
RUN apk add --no-cache curl \
&& rm -rf /var/cache/apk/*
# Do not install dev dependencies
RUN composer install --no-dev --optimize-autoloader
# BuildKit for parallel build
# export DOCKER_BUILDKIT=1
# docker buildx build --cache-from type=registry,ref=myapp:cache .
Production execution
# Build image
docker build -t myapp:v1.2.3 .
# Run
docker run -d \
--name myapp \
--restart unless-stopped \
-p 80:80 \
-e APP_ENV=production \
-e DB_HOST=db.internal \
--env-file .env.production \
-v /var/log/myapp:/var/www/html/storage/logs \
myapp:v1.2.3
# Update to new version (zero-downtime)
docker pull myapp:v1.3.0
docker stop myapp && docker rm myapp
docker run -d --name myapp ... myapp:v1.3.0
Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost/health || exit 1
GitHub Actions → Docker Registry
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
registry.example.com/myapp:latest
registry.example.com/myapp:${{ github.sha }}
cache-from: type=registry,ref=registry.example.com/myapp:cache
cache-to: type=registry,ref=registry.example.com/myapp:cache,mode=max
Timeline
Creating Dockerfile + registry setup + CI build: 2–3 days.







