Flask apps using Docker

This approach is cleaner, isolated, and scales much better than managing multiple Gunicorn + Nginx systemd services manually.

🧩 Overview

We’ll use:

  • Docker: runs each Flask app in its own container.
  • Gunicorn: runs Flask inside each container.
  • Nginx: reverse proxy that routes to each app container.
  • Docker Compose: orchestrates everything.

Your setup will look like this:

                     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
app1.example.com ───▢│  Flask App 1 β”‚
                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    β”‚
    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Nginx  │── app2.example.com ─▢ Flask App 2
β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜

βš™οΈ Step 1: Directory Structure

Let’s assume you’ll host two Flask apps: app1 and app2.

Create this structure:

multi-flask/
β”œβ”€β”€ nginx/
β”‚ └── nginx.conf
β”œβ”€β”€ app1/
β”‚ β”œβ”€β”€ app.py
β”‚ β”œβ”€β”€ requirements.txt
β”‚ └── Dockerfile
β”œβ”€β”€ app2/
β”‚ β”œβ”€β”€ app.py
β”‚ β”œβ”€β”€ requirements.txt
β”‚ └── Dockerfile
└── docker-compose.yml

🧱 Step 2: Each Flask App Setup

app1/app.py

from flask import Flask
app = Flask(**name**)

@app.route('/')
def home():
    return "Hello from App 1!"

if __name__ == '__main__':
    app.run(host='0.0.0.0')

app2/app.py

from flask import Flask
app = Flask(**name**)

@app.route('/')
def home():
    return "Hello from App 2!"

if __name__ == '__main__':
    app.run(host='0.0.0.0')

app1/requirements.txt and app2/requirements.txt

flask
gunicorn

🧰 Step 3: Dockerfile for Each App

app1/Dockerfile

FROM python:3.10-slim

WORKDIR /app
COPY . .
RUN pip install --no-cache-dir -r requirements.txt

CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]

Same for app2/Dockerfile.

🌐 Step 4: Nginx Configuration

Create nginx/nginx.conf:

events {}

http {
server {
listen 80;
server_name app1.example.com;

        location / {
            proxy_pass http://app1:5000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }

    server {
        listen 80;
        server_name app2.example.com;

        location / {
            proxy_pass http://app2:5000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }

}

🧩 Step 5: docker-compose.yml

Create docker-compose.yml in the root (multi-flask/):

version: "3.9"

services:
  app1:
    build: ./app1
    container_name: app1
    restart: always
    expose:
      - "5000"

  app2:
    build: ./app2
    container_name: app2
    restart: always
    expose:
      - "5000"

  nginx:
    image: nginx:latest
    container_name: nginx
    restart: always
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - app1
      - app2

πŸš€ Step 6: Run Everything

In the root directory:

docker-compose up -d --build

Check running containers:

docker ps

Visit:

http://app1.example.com β†’ App 1

http://app2.example.com β†’ App 2

πŸ”’ Step 7: Add HTTPS (Optional)

You can use Caddy or Traefik instead of Nginx for automatic HTTPS.

For example, using Caddy is as simple as:

caddy:
image: caddy
restart: always
ports: - "80:80" - "443:443"
volumes: - ./Caddyfile:/etc/caddy/Caddyfile
depends_on: - app1 - app2

And your Caddyfile:

app1.example.com {
    reverse_proxy app1:5000
}

app2.example.com {
    reverse_proxy app2:5000
}

Caddy will automatically fetch and renew SSL certificates. πŸ”₯

🧠 Summary

ComponentRole
FlaskThe app itself
GunicornRuns Flask app efficiently
Nginx / CaddyReverse proxy (and SSL if desired)
DockerIsolates each app
Docker ComposeOrchestrates all services

Leave a Reply