CodeWithAbdessamad

Deploying A Web App

Deploying a Web App

When scaling your applications from development to production, Docker provides a consistent, portable environment that eliminates “it works on my machine” headaches. This section walks you through deploying three common web application stacks—Node.js, PHP, and React—on a VPS host. Each example includes production-ready configurations, security best practices, and real-world deployment workflows.

Node.js App

Node.js applications thrive in Docker due to their lightweight, event-driven architecture. We’ll deploy a basic Express.js app with production optimizations.

Step 1: Create a minimal Express app

Start with a simple app that serves a “Hello World” response. This demonstrates core deployment patterns without unnecessary complexity.

<code class="language-bash">mkdir node-app && cd node-app
<p>npm init -y</p>
<p>npm install express</code>

Step 2: Build a production-ready Dockerfile

This Dockerfile includes Node.js 20, production build flags, and security hardening:

<code class="language-dockerfile"># Use a minimal Node base image with security updates
<p>FROM node:20.12.2-alpine</p>

<h1>Set working directory</h1>
<p>WORKDIR /app</p>

<h1>Copy package.json and install dependencies</h1>
<p>COPY package.json . </p>
<p>RUN npm install --production</p>

<h1>Copy application code</h1>
<p>COPY . .</p>

<h1>Expose port 3000 (default for Express)</h1>
<p>EXPOSE 3000</p>

<h1>Run the production server</h1>
<p>CMD ["node", "server.js"]</code>

Step 3: Deploy to your VPS

On your VPS (with Docker installed), follow these steps:

  1. Create a docker-compose.yml file:
<code class="language-yaml">version: '3.8'</p>

<p>services:</p>
<p>  web:</p>
<p>    build: .</p>
<p>    ports:</p>
<p>      - "3000:3000"</p>
<p>    environment:</p>
<p>      - NODE_ENV=production</p>
<p>    restart: always</p>
<p>    healthcheck:</p>
<p>      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]</p>
<p>      interval: 30s</p>
<p>      timeout: 10s</code>

  1. Run the deployment:
<code class="language-bash">docker-compose up -d</code>

Key production considerations

  • Health checks: Prevents Docker from restarting failed containers (critical for production stability)
  • Production flags: NODE_ENV=production triggers optimized builds and security hardening
  • Alpine base: Reduces image size by ~70% vs. Debian-based images
  • Port mapping: Exposes port 3000 to your VPS’s public IP for web traffic

💡 Pro tip: Always add a health check to your Dockerfile. This ensures your application is ready to handle traffic before Docker restarts it.

PHP App

PHP applications benefit from Docker’s isolation, especially when using modern PHP versions with Composer and production configuration.

Step 1: Create a PHP application

We’ll use a simple Laravel app for demonstration (though any PHP stack works). Initialize with:

<code class="language-bash">mkdir php-app && cd php-app
<p>composer init -y</p>
<p>composer require laravel/laravel</code>

Step 2: Build a secure PHP Docker image

This configuration uses PHP 8.2, FPM (for better performance than CLI), and security best practices:

<code class="language-dockerfile"># Use official PHP 8.2 FPM image with security updates
<p>FROM php:8.2-fpm</p>

<h1>Set working directory</h1>
<p>WORKDIR /var/www</p>

<h1>Install PHP extensions needed for production</h1>
<p>RUN docker-php-extension-installer redis mbstring pdo_mysql</p>

<h1>Copy composer.json and install dependencies</h1>
<p>COPY composer.json composer.json</p>
<p>RUN composer install --no-dev</p>

<h1>Copy application code</h1>
<p>COPY . .</p>

<h1>Configure PHP for production</h1>
<p>COPY php.ini /etc/php/8.2/fpm/php.ini</p>

<h1>Set environment variables</h1>
<p>ENV DISPLAY_NAME="Production"</p>
<p>ENV APP_ENV=production</p>

<h1>Run as non-root user for security</h1>
<p>USER 999:999</p>

<h1>Expose port 9000 (FPM default)</h1>
<p>EXPOSE 9000</code>

Step 3: Deploy to your VPS

On your VPS:

  1. Create a docker-compose.yml:
<code class="language-yaml">version: '3.8'</p>

<p>services:</p>
<p>  web:</p>
<p>    build: .</p>
<p>    ports:</p>
<p>      - "9000:9000"</p>
<p>    environment:</p>
<p>      - APP_ENV=production</p>
<p>    restart: always</p>
<p>    healthcheck:</p>
<p>      test: ["CMD", "curl", "-f", "http://localhost:9000/health"]</p>
<p>      interval: 30s</p>
<p>      timeout: 10s</code>

  1. Run the deployment:
<code class="language-bash">docker-compose up -d</code>

Critical security enhancements

Feature Why it matters Example
USER 999:999 Prevents privilege escalation Runs as non-root user
php.ini Configures security settings Disables unsafe functions
restart: always Ensures service resilience Auto-restarts on failure
healthcheck Prevents traffic to unhealthy services 30s interval, 10s timeout

⚠️ Warning: Always disable display_errors in production PHP configs. This prevents sensitive errors from leaking to users.

React Build

React applications are typically static assets after build. We’ll deploy a production build using Docker for consistent delivery.

Step 1: Create a React app

Start with a basic React project:

<code class="language-bash">npx create-react-app react-app
<p>cd react-app</p>
<p>npm install</code>

Step 2: Build for production

Generate a production-ready static build:

<code class="language-bash">npm run build</code>

Step 3: Dockerize the static build

This Dockerfile serves the React build with Nginx for production performance:

<code class="language-dockerfile"># Use lightweight Nginx base image
<p>FROM nginx:alpine</p>

<h1>Copy production build</h1>
<p>COPY build /usr/share/nginx/html</p>

<h1>Configure Nginx for production</h1>
<p>COPY nginx.conf /etc/nginx/conf.d/default.conf</p>

<h1>Set security headers</h1>
<p>RUN echo "add_header X-Content-Type-Options nosniff;" >> /etc/nginx/conf.d/default.conf</p>
<p>RUN echo "add_header X-Frame-Options DENY;" >> /etc/nginx/conf.d/default.conf</p>

<h1>Expose port 80 (HTTP)</h1>
<p>EXPOSE 80</code>

Step 4: Deploy to your VPS

On your VPS:

  1. Create nginx.conf:
<code class="language-nginx">server {</p>
<p>    listen 80;</p>
<p>    server<em>name </em>;</p>

<p>    location / {</p>
<p>        root /usr/share/nginx/html;</p>
<p>        index index.html;</p>
<p>        try_files $uri $uri/ /index.html;</p>
<p>    }</p>
<p>}</code>

  1. Run the deployment:
<code class="language-bash">docker-compose up -d</code>

Why this works for React

  • Client-side rendering: React builds to static files (no server-side rendering needed)
  • Nginx optimization: Handles static assets efficiently with cache control
  • Security headers: Prevents common attacks like XSS and clickjacking
  • 404 handling: try_files ensures clean 404 responses for client-side routes

Pro tip: For larger React apps, use Docker’s multi-stage build to reduce image size by ~50% (we’ll skip this for brevity but it’s a key production practice).

Summary

You’ve now deployed three critical web application stacks using Docker on VPS hosting:

  1. Node.js apps benefit from lightweight containers with health checks and production optimizations
  2. PHP apps leverage Dockerized FPM with security-hardened configurations and proper environment variables
  3. React builds work best as static assets served through Nginx with security headers

Each deployment follows identical patterns: Dockerfile configuration, docker-compose orchestration, and health checks for production resilience. The key takeaway is consistency—your development environment becomes identical to production, eliminating configuration drift. Remember to always test deployments in staging before production, and monitor your VPS with tools like docker stats to ensure smooth operation. 🚀