# Docker-based Web Application Setup (example) ## Folder Structure ```bash . ├── /nginx.sites/ # Server configuration for each of web apps ├── /nginx.snippets/ # Nginx code snippets ├── docker-compose.yml # Defines Docker services, networks and volumes └── nginx.config # Top-level Nginx configuration ``` ### `docker-compose.yml` ```yml version: '3' volumes: data: redis: services: nginx: image: nginx:1.11.10-alpine links: - api - web read_only: true tmpfs: - /var/cache/nginx - /var/log/nginx - /var/run volumes: - ./nginx.snippets:/etc/nginx/snippets:ro - ./nginx.sites:/etc/nginx/sites-enabled:ro - ./nginx.conf:/etc/nginx/nginx.conf:ro - /etc/letsencrypt:/etc/letsencrypt:ro - /etc/ssl/certs/dhparam.pem:/etc/ssl/certs/dhparam.pem:ro - /var/www:/var/www ports: - '80:80' - '443:443' db: image: postgres:9.6.2-alpine restart: always environment: - POSTGRES_PASSWORD=xxx read_only: true tmpfs: - /tmp - /var/run/postgresql volumes: - data:/var/lib/postgresql/data redis: image: redis:3.2.8-alpine restart: always read_only: true volumes: - redis:/data web: image: web read_only: true restart: always environment: - PORT=3000 - NODE_ENV=production expose: - '3000' api: image: api read_only: true restart: always depends_on: - db - redis links: - db - redis environment: - PORT=3000 - NODE_ENV=production - NODE_DEBUG=false - WEBSITE_URL=https://example.com - FRONTEND_HOST_WHITELIST=localhost - DATABASE_URL=postgres://user:xxx@db:5432/api - DATABASE_DEBUG=false - REDIS_URL=redis://redis:6379/1 - SESSION_SECRET=xxx - FACEBOOK_ID=xxx - FACEBOOK_SECRET=xxx - GOOGLE_ID=xxx - GOOGLE_SECRET=xxx - TWITTER_KEY=xxx - TWITTER_SECRET=xxx expose: - '3000' # ports: # - '0.0.0.0:9229:9229' # V8 inspector ``` ### `nginx.sites/example.com` ```txt server { listen 80; listen [::]:80; server_name example.com www.example.com; return 301 https://example.com$request_uri; } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name www.example.com; include snippets/ssl-example.com.conf; return 301 https://example.com$request_uri; } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name example.com; include snippets/ssl-example.com.conf; root /var/www/example.com; location ~ /.well-known { allow all; } location ~ ^/login/.+ { include snippets/proxy-params.conf; proxy_pass http://api:3000; } location /graphql { include snippets/proxy-params.conf; proxy_pass http://api:3000; } location / { include snippets/proxy-params.conf; proxy_pass http://web:3000; } } ``` ### `nginx.snippets/proxy-params.conf` ```txt proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_max_temp_file_size 0; proxy_redirect off; proxy_read_timeout 240s; ``` ### `nginx.snippets/ssl-params.conf` ```txt # https://cipherli.st/ # https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"; ssl_ecdh_curve secp384r1; ssl_session_cache shared:SSL:10m; ssl_session_tickets off; ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; # Disable preloading HSTS for now. You can use the commented out header line that includes # the "preload" directive if you understand the implications. #add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"; add_header Strict-Transport-Security "max-age=63072000; includeSubdomains"; add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; ssl_dhparam /etc/ssl/certs/dhparam.pem; ``` ### `ssl-example.com.conf` ```txt include snippets/ssl-params.conf; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; ```