Caddy Reverse Proxy Configuration Issue with Docker Compose and OpenVidu

1. The problem I’m having:

I am using Docker Compose to set up OpenVidu, and within that setup, I have Caddy configured as the reverse proxy. I have set up Caddy to handle both the root address (/) and the /api path. However, I am encountering an issue where requests to /api are not being routed correctly, and instead, only the root address (/) seems to be functioning as expected.

could someone help me understand why this might be happening and how to fix it so that both /api and the root path are handled correctly?

3. Caddy version:

v2.7.6 h1:w0NymbG2m9PcvKWsrXO6EEkY9Ru4FJK8uQbYcev1p3A=

4. How I installed and ran Caddy:

a. System environment:

Distributor ID: Ubuntu
Description:    Ubuntu 20.04.6 LTS
Release:        20.04
Codename:       focal

b. Command:

docker compose up -d 

c. Service/unit/compose file:

services:
  caddy:
    image: docker.io/openvidu/openvidu-caddy:3.0.0-beta2
    restart: unless-stopped
    container_name: caddy
    network_mode: host
    command: run --config /etc/caddy.yaml --adapter yaml
    volumes:
      - ./custom-layout:/var/www/custom-layout
      - ./config/caddy.yaml:/etc/caddy.yaml
      - ./data/caddy_data:/data
      - ./owncert:/owncert
    logging:
      options:
        max-size: "${DOCKER_LOGS_MAX_SIZE:-200M}"
    labels:
      - logging=true

d. My complete Caddy config:

 Caddy configuration for OpenVidu
# to allow access to OpenVidu services
storage:
  "module": "file_system"
  "root": "/data"
apps:
  tls:
    certificates:
      automate:
        - http://i11c203.p.ssafy.io
    automation:
      policies:
        - issuers:
          - module: acme
            email: jmeve24@naver.com
  layer4:
    servers:
      public:
        listen:
          - ":443"
        routes:
          - match:
              - tls:
                  sni:
                    - http://i11c203.p.ssafy.io
            handle:
              - handler: tls
                connection_policies:
                  - alpn:
                      - http/1.1
              - handler: proxy
                upstreams:
                  - dial:
                    - "localhost:7880"
      # RTMP Ingress
      rtmp:
        listen:
          - ":1935"
        routes:
          - match:
              - tls:
                  sni:
                    - myserverurl
            handle:
              - handler: tls
              - handler: proxy
                load_balancing:
                  selection:
                    policy: round_robin
                upstreams:
                    - dial:
                      - localhost:1945
  http:
    servers:
      public:
        listen:
          - ":7880"
        logs:
          default_logger_name: default
        routes:
          - handle:
              - handler: subroute
                routes:
                  - handle:
                    - handler: reverse_proxy
                      health_checks:
                        active:
                          expect_status: 200
                          interval: 5s
                          timeout: 2s
                          uri: "/"
                      load_balancing:
                        retries: 2
                        selection_policy:
                          policy: round_robin
                      upstreams:
                      - dial: localhost:7780
                    match:
                      - path:
                        - "/twirp/*"
                        - "/rtc"
          # Custom layouts.
          # If you want to use any recording layout, you can place it in /opt/openvidu/custom-layout
          # and it will be served in https://<your-domain>/openvidu/layouts
          # or internally at https://localhost:7880/openvidu/layouts
          - handle:
              - handler: subroute
                routes:
                  - handle:
                      - handler: rewrite
                        strip_path_prefix: /openvidu/layouts
                      - handler: file_server
                        root: /var/www/custom-layout
                    match:
                      - path:
                        - /openvidu/layouts
                        - /openvidu/layouts/*
          # Ingress WHIP service
          - handle:
              - handler: subroute
                routes:
                  - handle:
                      - handler: reverse_proxy
                        health_checks:
                          active:
                            expect_status: 404
                            interval: 5s
                            timeout: 2s
                            uri: /
                        load_balancing:
                          retries: 2
                          selection_policy:
                            policy: round_robin
                        upstreams:
                          - dial: "localhost:8080"
                    match:
                      - path:
                          - /whip
          # Dashboard
          - handle:
              - handler: subroute
                routes:
                  - handle:
                      - handler: rewrite
                        strip_path_prefix: /dashboard
                      - handler: reverse_proxy
                        upstreams:
                          - dial: "localhost:5000"
                    match:
                      - path:
                          - /dashboard/*
                          - /dashboard
          # Health check caddy
          - handle:
              - handler: subroute
                routes:
                  - handle:
                      - handler: static_response
                        status_code: 200
                        body: "OK"
                    match:
                      - path:
                          - /health/caddy
          # Minio Console
          - handle:
              - handler: subroute
                routes:
                  - handle:
                      - handler: rewrite
                        strip_path_prefix: /minio-console
                      - handler: reverse_proxy
                        upstreams:
                          - dial: "localhost:9101"
                    match:
                      - path:
                          - /minio-console/*
                          - /minio-console
          # Grafana
          - handle:
              - handler: subroute
                routes:
                  - handle:
                      - handler: reverse_proxy
                        upstreams:
                          - dial: "localhost:3000"
                   match:
                      - path:
                          - /grafana/*
                          - /grafana
          # Demo App
          - handle:
              - handler: subroute
                routes:
                  - handle:
                      - handler: reverse_proxy
                        upstreams:
                          - dial: "localhost:8000"
                    match:
                      - path:
                          - /api/*
                          - /api


          - handle:
              - handler: subroute
                routes:
                  - handle:
                      - handler: reverse_proxy
                        upstreams:
                          - dial: "localhost:5173"
                    match:
                      - path:
                          - /*
                          - /


      minio:
        listen:
          - ":9000"
        logs:
          default_logger_name: default
        routes:
          - handle:
              - handler: reverse_proxy
                health_checks:
                  active:
                    expect_status: 200
                    interval: 5s
                    timeout: 2s
                    uri: "/minio/health/live"
                upstreams:
                  - dial: "localhost:9100"
            match:
              - host:
                  - i11c203.p.ssafy.io

logging:
  logs:
    default:
      level: INFO
      encoder:
        format: filter
        wrap:
          format: json
        fields:
          "request>headers":
            filter: delete
          "resp_headers":
            filter: delete
          "request>uri":
            filter: query
            actions:
              - parameter: "access_token"
                type: delete
      include:
        - http
      writer:
        output: stdout
    layer4access:
      level: DEBUG
      include:
        - layer4
      writer:
        output: stdout

I have added this part:

          # Demo App
          - handle:
              - handler: subroute
                routes:
                  - handle:
                      - handler: reverse_proxy
                        upstreams:
                          - dial: "localhost:8000"
                    match:
                      - path:
                          - /api/*
                          - /api


          - handle:
              - handler: subroute
                routes:
                  - handle:
                      - handler: reverse_proxy
                        upstreams:
                          - dial: "localhost:5173"
                    match:
                      - path:
                          - /*
                          - /

5. Links to relevant resources:

Do you have any specific reason for using YAML config? You can use Caddyfile now, caddy-l4 just had Caddyfile support added.

Names to automate should not include a scheme.

Similarly, SNI matchers should not include a scheme. Only the hostname.

Technically, Caddy has to enter every subroute in your config, because your matcher is on the inner routes. I think you can move your matchers up to a higher level of your config structure.

This matcher is tautological, it matches everything. You can just remove the matcher.

3 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.