Skip to main content
ipMoodle uses Nginx as a reverse proxy to handle HTTP requests and forward PHP processing to the PHP-FPM container. The configuration is optimized for Moodle’s requirements.

Configuration file

The Nginx configuration is located at nginx/Default.conf and mounted into the web container:
docker-compose.yml:59
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro

Full configuration

nginx/Default.conf
server {
    listen 80;
    #server_name eva.intiinside.com;
    root /var/www/html/public;
    index index.php;

    # Optimización de buffers y tiempos
    client_max_body_size 512M;
    fastcgi_read_timeout 300;
    
    # Slash Arguments (VITAL para Moodle)
    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    # Procesamiento PHP pasando al contenedor "app"
    location ~ [^/]\.php(/|$) {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    # Seguridad: Bloquear acceso a archivos sensibles
    location ~ (/vendor/|/node_modules/|composer\.json|/readme|/README|/LICENSE|/\.git) {
        deny all;
        return 404;
    }
}

Key settings

Server block

listen
integer
default:"80"
Port on which Nginx listens for HTTP connectionsThis is the internal container port. The docker-compose.yml maps this to the host’s port 80.
server_name
string
Domain name for this server blockCurrently commented out (#server_name eva.intiinside.com). Uncomment and set your domain for production deployments.
Required when using SSL/TLS certificates or hosting multiple sites
root
string
default:"/var/www/html/public"
Document root directoryNote: Moodle’s actual root is /var/www/html, not /var/www/html/public. You should change this to:
root /var/www/html;

Upload optimization

nginx/Default.conf:8
client_max_body_size 512M;
client_max_body_size
string
default:"512M"
Maximum size of the client request body (file uploads)This must match or exceed PHP’s upload_max_filesize and post_max_size settings (both 512M in the Dockerfile).Increase for large video uploads:
client_max_body_size 2048M;

PHP-FPM timeout

nginx/Default.conf:9
fastcgi_read_timeout 300;
fastcgi_read_timeout
string
default:"300"
Timeout in seconds for reading a response from the FastCGI server (PHP-FPM)300 seconds (5 minutes) allows time for long-running PHP operations like course backups.Should be less than or equal to PHP’s max_execution_time (600 seconds in Dockerfile)

Slash arguments (critical for Moodle)

nginx/Default.conf:12-14
location / {
    try_files $uri $uri/ /index.php$is_args$args;
}
This configuration enables slash arguments, a feature Moodle requires for clean URLs like /course/view.php/123 instead of /course/view.php?id=123.
Do not remove or modify this try_files directive. Moodle will not work correctly without slash argument support.

PHP processing

nginx/Default.conf:17-24
location ~ [^/]\.php(/|$) {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass app:9000;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
}
Key elements:
  • fastcgi_split_path_info: Splits the URI into script name and path info (required for slash arguments)
  • fastcgi_pass app:9000: Forwards PHP requests to the app container on port 9000 (PHP-FPM)
  • SCRIPT_FILENAME and PATH_INFO: Ensures correct file paths are passed to PHP

Security blocks

nginx/Default.conf:27-30
location ~ (/vendor/|/node_modules/|composer\.json|/readme|/README|/LICENSE|/\.git) {
    deny all;
    return 404;
}
Blocks direct access to sensitive files and directories that should not be publicly accessible.

Modifying Nginx configuration

1

Edit the configuration file

nano nginx/Default.conf
Make your changes and save.
2

Test the configuration

docker compose exec web nginx -t
Expected output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
3

Reload Nginx

docker compose exec web nginx -s reload
Or restart the container:
docker compose restart web

Common customizations

Enable server name

For production deployments with a domain:
server {
    listen 80;
    server_name moodle.example.com;
    # ... rest of configuration
}

Increase upload limit

For large video or backup files:
client_max_body_size 2048M;
Remember to also increase PHP’s upload_max_filesize and post_max_size in the Dockerfile

Add access logging

server {
    listen 80;
    access_log /var/log/nginx/moodle-access.log;
    error_log /var/log/nginx/moodle-error.log;
    # ... rest of configuration
}
View logs:
docker compose logs web

HTTP to HTTPS redirect

When SSL is configured (see SSL/HTTPS guide):
server {
    listen 80;
    server_name moodle.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name moodle.example.com;
    
    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;
    
    # ... rest of configuration
}

Custom error pages

error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;

location = /404.html {
    root /usr/share/nginx/html;
    internal;
}

location = /50x.html {
    root /usr/share/nginx/html;
    internal;
}

Gzip compression

Add compression for better performance:
server {
    # ... other settings
    
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css text/xml text/javascript 
               application/x-javascript application/xml+rss 
               application/javascript application/json;
    gzip_disable "MSIE [1-6]\.";
}

Performance tuning

Worker processes

The base nginx:alpine image uses default worker settings. For high-traffic sites, create a custom nginx.conf:
worker_processes auto;
worker_connections 1024;

events {
    use epoll;
    multi_accept on;
}

http {
    # ... include your site configs
}
Mount this as a volume:
web:
  volumes:
    - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
    - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro

Caching static files

Add browser caching for static assets:
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}
Be cautious with aggressive caching during development or frequent theme changes

Troubleshooting

This means Nginx cannot reach PHP-FPM. Check:
  1. App container is running: docker compose ps app
  2. PHP-FPM is listening on port 9000: docker compose exec app netstat -tlnp | grep 9000
  3. Containers are on the same network: docker network inspect ipmoodle_moodle-net
  4. fastcgi_pass points to correct service: app:9000
The uploaded file exceeds client_max_body_size. Increase the limit:
client_max_body_size 1024M;
Then reload: docker compose exec web nginx -s reload
PHP script execution exceeded fastcgi_read_timeout. Increase the timeout:
fastcgi_read_timeout 600;
Also ensure PHP’s max_execution_time is at least this value.
Check syntax errors:
docker compose exec web nginx -t
Common issues:
  • Missing semicolons
  • Incorrect directive names
  • Unclosed blocks ({ without })
  1. Check document root is correct: /var/www/html (not /var/www/html/public)
  2. Verify files exist in volume: docker compose exec web ls -la /var/www/html/theme
  3. Check file permissions: should be readable by nginx (www-data user)

Next steps

SSL/HTTPS setup

Configure SSL certificates for secure connections

PHP settings

Align PHP timeouts with Nginx settings

Performance monitoring

Monitor Nginx access logs and performance

Scaling

Load balancing with multiple Nginx instances

Build docs developers (and LLMs) love