John Kealy
John Kealy

Reputation: 1883

Caddy server: Running a Django app behind a reverse proxy with docker-compose

I just discovered CaddyServer, and it looks super promising for running multiple applications on the same VPS with Docker.

But I'm struggling to set my docker-compose.yml file(s) to work with external domains. I'll use gunicorn later in production, for now I'd just be happy to get the Django runserver to work.

Here's my directory structure:

caddy/
    Caddyfile
    docker-compose.yml
djangoApp/
    docker-compose.yml
    Dockerfile
    config/
       ...
    manage.py
    requirements.txt
myghostapp/
    

This is caddy/Caddyfile

{
    email [email protected] 
    acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}

app1.example.com {
    reverse_proxy django:8000
}

# app2.example.com {
#     reverse_proxy ghost:2368
# }

app2.example.com is my intended second domain, which points to a ghost application. This one works when uncommented (I'm including it for reference).

This is caddy/docker-compose (the reverse proxy)

version: "3"

networks:
    web:
        external: true

services:
    caddy:
        image: caddy:2-alpine
        restart: unless-stopped
        ports:
            - "80:80"
            - "443:443"
        volumes:
            - /data/caddy/Caddyfile:/etc/caddy/Caddyfile
        networks:
            - web

    django:
        build:
            context: /data/djangoApp/
            dockerfile: /data/djangoApp/Dockerfile
        restart: unless-stopped
        environment:
            - url=app1.example.com 
        volumes:
            - /data/djangoApp:/app/
        networks:
            - web

    # ghost:
    #     image: ghost:3.22-alpine
    #     restart: unless-stopped
    #     environment:
    #         - url=app2.example.com 
    #     volumes:
    #         - /data/myghostapp:/var/lib/ghost/content
    #     networks:
    #         - web

For now, it would be great to get the Django app working.

In the djangoApp folder, I have a Dockerfile like this:

# djangoApp/Dockerfile
FROM python:3

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

RUN mkdir /app
WORKDIR /app
COPY requirements.txt /app/
RUN pip install -r requirements.txt
COPY . /app/

And also a docker-compose.yml

# djangoApp/docker-compose.yml
version: "3.8"

services:
  db:
    image: postgres:10.5-alpine
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_DB=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    networks:
      - web
      
  djangy:
    build: .
    command: bash -c "python /app/manage.py migrate --noinput && python /app/manage.py runserver 0.0.0.0:8000"
    volumes:
      - .:/app
    ports:
      - "8000:8000"
    depends_on:
      - db
    networks:
      - web
    links:
      - db:db  

volumes:
  postgres_data:

networks:
  web:
    external: true

I've been assuming that on my VPS, I could simply docker create network web and then run docker-compose up --build inside caddy/, followed by the same inside djangoApp/

but I get lookup django on 127.0.0.11:53: no such host for the error msg in my logs when I try to hit the domain in my browser.

Any help would be much appreciated!

Upvotes: 0

Views: 5815

Answers (1)

John Kealy
John Kealy

Reputation: 1883

For anyone that finds this while debugging their docker-compose setup, the main misunderstanding I had when I posted it was that I thought I needed several docker-compose files. Since the whole point of a reverse proxy is that everything needs to share port 80, that assumption seems silly now...

Here is my current working setup:

version: "3"

networks:
    web:
        external: true

volumes:
    postgres_data:


services:
    # CaddyServer reverse proxy
    caddy:
        restart: always
        image: caddy:2-alpine
        ports:
            - "80:80"
            - "443:443"
        volumes:
            - /local/path/to/Caddyfile:/path/inside/continer/to/Caddyfile
        networks:
            - web

    # Django web app
    django:
        restart: always
        build:
            context: .
            dockerfile: /local/path/to/Dockerfile
        expose:
            - 8000
        environment:
            - url=https://api.backend.example.com
        command:
            "gunicorn config.wsgi:application --bind 0.0.0.0:8000"
        env_file:
            - /home/jokea/flora/config/.env.django.prod
        depends_on:
            - db
        networks:
            - web


    # Django database
    db:
        restart: always
        image: postgres:12.0-alpine
        volumes:
            - postgres_data:/var/lib/path/inside/container/postgresql/data/
        env_file:
            - /path/to/environment/variables/file/.env.django.prod.db
        networks:
            - web

    # Nuxt frontend app
    nuxt:
        build: .
        ports:
            - "3000:3000"
        environment:
            - url=https://wwww.example.com
        volumes:
            - ./frontend:/var/lib/frontend
        command:
            "npm run start"
        env_file:
            - ./.env.django.prod
        networks:
            - web

It won't work out of the box but hopefully it can provide some clues. The Caddyfile is


{
    email [email protected]
    # acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}

https://api.backend.example.com{
    reverse_proxy django:8000
}

https://www.example.com  {
    reverse_proxy nuxt:3000
}

A critical element to using docker-compose to create a reverse proxy is the external network (here I called this web). With this, you can provide access to the http and https ports while using domain names to redirect to the correct application – this is how the reverse proxy works.

I can't recommend CaddyServer enough, it's awesome.

Upvotes: 3

Related Questions