BLOG POSTS
How to Run Nginx in a Docker Container on Ubuntu 24

How to Run Nginx in a Docker Container on Ubuntu 24

Running Nginx in a Docker container on Ubuntu 24 combines the flexibility of containerization with the robust performance of one of the most popular web servers. This approach simplifies deployment, ensures consistent environments across development and production, and makes scaling web applications significantly easier. You’ll learn how to set up Docker on Ubuntu 24, pull and run Nginx containers, configure custom settings, handle persistent data, and troubleshoot common issues that developers encounter in real-world scenarios.

Understanding Docker and Nginx Integration

Docker containers provide an isolated environment that packages Nginx with all its dependencies, eliminating the “works on my machine” problem. When you run Nginx in Docker, you’re essentially creating a lightweight virtual machine that contains only what’s necessary to run the web server. This approach offers several advantages over traditional installations:

  • Consistent behavior across different Ubuntu systems
  • Easy rollback to previous versions
  • Simplified SSL certificate management
  • Isolated configuration that doesn’t conflict with system packages
  • Rapid deployment and scaling capabilities

The Docker daemon communicates with the Nginx process inside the container through Linux namespaces and cgroups, providing resource isolation while maintaining near-native performance. Network traffic flows through Docker’s bridge network by default, though you can configure custom networks for more complex setups.

Prerequisites and Docker Installation

Before diving into Nginx containers, you need Docker installed on your Ubuntu 24 system. The installation process has been streamlined compared to earlier Ubuntu versions, but there are still some gotchas to watch out for.

First, update your package index and install required dependencies:

sudo apt update
sudo apt install apt-transport-https ca-certificates curl software-properties-common

Add Docker’s official GPG key and repository:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Install Docker Engine:

sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Add your user to the docker group to avoid using sudo with every Docker command:

sudo usermod -aG docker $USER
newgrp docker

Verify the installation works correctly:

docker --version
docker run hello-world

Running Your First Nginx Container

The simplest way to get Nginx running is with a single Docker command. The official Nginx image from Docker Hub comes pre-configured and ready to serve content immediately.

Pull and run the latest Nginx image:

docker run -d --name my-nginx -p 8080:80 nginx:latest

This command does several things:

  • -d runs the container in detached mode (background)
  • --name my-nginx assigns a friendly name to the container
  • -p 8080:80 maps port 8080 on your Ubuntu host to port 80 inside the container
  • nginx:latest specifies the image to use

Visit http://localhost:8080 in your browser, and you should see the default Nginx welcome page. If you’re working on a remote server, replace localhost with your server’s IP address.

Check the container status:

docker ps

View container logs:

docker logs my-nginx

Stop and remove the container when you’re done testing:

docker stop my-nginx
docker rm my-nginx

Custom Configuration and File Mounting

Running Nginx with default settings is fine for testing, but real applications need custom configurations. Docker volumes and bind mounts let you override default files and persist data between container restarts.

Create a custom Nginx configuration file on your Ubuntu system:

mkdir -p ~/nginx-config
nano ~/nginx-config/nginx.conf

Add a basic configuration:

events {
    worker_connections 1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    
    sendfile        on;
    keepalive_timeout  65;
    
    server {
        listen       80;
        server_name  localhost;
        
        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
        }
        
        location /api/ {
            proxy_pass http://backend:3000/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
        
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    }
}

Create a directory for your web content:

mkdir -p ~/nginx-html
echo "

Hello from Custom Nginx!

" > ~/nginx-html/index.html

Run Nginx with your custom configuration and content:

docker run -d \
  --name custom-nginx \
  -p 8080:80 \
  -v ~/nginx-config/nginx.conf:/etc/nginx/nginx.conf:ro \
  -v ~/nginx-html:/usr/share/nginx/html:ro \
  nginx:latest

The :ro flag makes the mounts read-only, which is a security best practice for configuration files.

Docker Compose for Complex Setups

When your setup involves multiple containers or complex networking, Docker Compose simplifies management. Here’s a practical example that runs Nginx alongside a Node.js application:

Create a docker-compose.yml file:

version: '3.8'

services:
  nginx:
    image: nginx:latest
    container_name: nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./html:/usr/share/nginx/html:ro
      - ./ssl:/etc/nginx/ssl:ro
    depends_on:
      - backend
    networks:
      - web-network

  backend:
    image: node:18-alpine
    container_name: node-backend
    working_dir: /app
    volumes:
      - ./app:/app
    command: "npm start"
    expose:
      - "3000"
    networks:
      - web-network

networks:
  web-network:
    driver: bridge

Start the entire stack:

docker-compose up -d

Monitor logs from all services:

docker-compose logs -f

Scale the backend service:

docker-compose up -d --scale backend=3

Performance Optimization and Resource Management

Docker containers inherit performance characteristics from the host system, but you can optimize resource usage and Nginx performance through proper configuration.

Configuration Default Value Recommended for Production Impact
Worker Processes 1 auto (matches CPU cores) Higher concurrency
Worker Connections 1024 4096-8192 More concurrent clients
Memory Limit Unlimited 512MB-2GB Prevents memory exhaustion
CPU Limit Unlimited 1-2 cores Fair resource sharing

Run Nginx with resource constraints:

docker run -d \
  --name nginx-optimized \
  --memory=512m \
  --cpus=1.5 \
  --restart=unless-stopped \
  -p 80:80 \
  -v ~/nginx-config/nginx.conf:/etc/nginx/nginx.conf:ro \
  nginx:latest

Monitor resource usage:

docker stats nginx-optimized

For high-traffic scenarios, consider using the Alpine-based Nginx image, which uses significantly less memory:

docker run -d --name nginx-alpine -p 8080:80 nginx:alpine

SSL/TLS Configuration and Security

Implementing HTTPS in containerized Nginx requires careful certificate management. You can use Let’s Encrypt certificates or self-signed certificates for development.

Generate self-signed certificates for testing:

mkdir -p ~/nginx-ssl
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout ~/nginx-ssl/nginx.key \
  -out ~/nginx-ssl/nginx.crt \
  -subj "/C=US/ST=State/L=City/O=Organization/CN=localhost"

Create an SSL-enabled Nginx configuration:

events {
    worker_connections 1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    
    # Security headers
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    
    server {
        listen 80;
        server_name localhost;
        return 301 https://$server_name$request_uri;
    }
    
    server {
        listen 443 ssl http2;
        server_name localhost;
        
        ssl_certificate /etc/nginx/ssl/nginx.crt;
        ssl_certificate_key /etc/nginx/ssl/nginx.key;
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
        ssl_prefer_server_ciphers off;
        
        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
        }
    }
}

Run Nginx with SSL support:

docker run -d \
  --name nginx-ssl \
  -p 80:80 \
  -p 443:443 \
  -v ~/nginx-config/ssl-nginx.conf:/etc/nginx/nginx.conf:ro \
  -v ~/nginx-ssl:/etc/nginx/ssl:ro \
  -v ~/nginx-html:/usr/share/nginx/html:ro \
  nginx:latest

Common Issues and Troubleshooting

Running Nginx in Docker containers can present unique challenges. Here are the most frequent issues and their solutions:

Permission Denied Errors:

If you encounter permission issues with mounted files, check the ownership and permissions:

# Fix ownership issues
sudo chown -R $USER:$USER ~/nginx-config ~/nginx-html

# Set proper permissions
chmod 644 ~/nginx-config/nginx.conf
chmod -R 755 ~/nginx-html

Port Already in Use:

When port 80 or 443 is already occupied, identify and stop conflicting services:

# Check what's using port 80
sudo netstat -tlnp | grep :80

# Stop Apache if it's running
sudo systemctl stop apache2
sudo systemctl disable apache2

Configuration Syntax Errors:

Test your Nginx configuration before deploying:

docker run --rm -v ~/nginx-config/nginx.conf:/etc/nginx/nginx.conf:ro nginx:latest nginx -t

Container Won’t Start:

Check container logs for detailed error messages:

docker logs container-name
docker inspect container-name

Networking Issues:

When containers can’t communicate, verify network configuration:

# List Docker networks
docker network ls

# Inspect network details
docker network inspect bridge

# Create custom network
docker network create my-network

Real-World Use Cases and Integration

Containerized Nginx excels in several production scenarios. Here are practical applications that demonstrate its versatility:

Microservices Gateway:

Use Nginx as an API gateway that routes requests to different microservices:

upstream user-service {
    server user-api:3001;
}

upstream order-service {
    server order-api:3002;
}

upstream product-service {
    server product-api:3003;
}

server {
    listen 80;
    
    location /api/users/ {
        proxy_pass http://user-service/;
    }
    
    location /api/orders/ {
        proxy_pass http://order-service/;
    }
    
    location /api/products/ {
        proxy_pass http://product-service/;
    }
}

Load Balancer Configuration:

Distribute traffic across multiple backend servers with health checks:

upstream backend {
    server web1:80 max_fails=3 fail_timeout=30s;
    server web2:80 max_fails=3 fail_timeout=30s;
    server web3:80 backup;
}

server {
    listen 80;
    
    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 30s;
        proxy_send_timeout 30s;
        proxy_read_timeout 30s;
    }
}

Static File Serving with Caching:

Optimize performance for static assets:

server {
    listen 80;
    root /usr/share/nginx/html;
    
    # Cache static assets
    location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        try_files $uri =404;
    }
    
    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
}

Best Practices and Security Considerations

Production deployments require attention to security and operational best practices. These guidelines help ensure reliable and secure Nginx containers:

Use Non-Root User:

Create a custom Dockerfile that runs Nginx as a non-privileged user:

FROM nginx:alpine

# Create nginx user
RUN addgroup -g 1001 -S nginx && \
    adduser -S -D -H -u 1001 -h /var/cache/nginx -s /sbin/nologin -G nginx -g nginx nginx

# Set proper permissions
RUN chown -R nginx:nginx /var/cache/nginx && \
    chown -R nginx:nginx /var/log/nginx && \
    chown -R nginx:nginx /etc/nginx/conf.d

RUN touch /var/run/nginx.pid && \
    chown -R nginx:nginx /var/run/nginx.pid

USER nginx

EXPOSE 8080

CMD ["nginx", "-g", "daemon off;"]

Regular Updates and Vulnerability Scanning:

Keep your Nginx images updated and scan for vulnerabilities:

# Pull latest updates
docker pull nginx:latest

# Scan for vulnerabilities (if you have Docker Scout)
docker scout cves nginx:latest

Resource Monitoring and Logging:

Implement proper logging and monitoring for production systems:

docker run -d \
  --name nginx-monitored \
  --log-driver=json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  -p 80:80 \
  nginx:latest

For comprehensive server hosting solutions that complement your containerized applications, consider exploring VPS services for scalable container deployments or dedicated servers for high-performance production environments.

The combination of Docker containers and proper hosting infrastructure provides a robust foundation for modern web applications. Regular monitoring, security updates, and performance optimization ensure your Nginx containers deliver reliable service while maintaining the flexibility that makes containerization so valuable for development and deployment workflows.



This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.

This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.

Leave a reply

Your email address will not be published. Required fields are marked