Api-platform docker deployment behind nginx-proxy

Viewed 274

I'm trying to deploy a quick demo of api-platform. In advance, my apologize if I missed something in a discssion or a documentation, I'm not used to work on deployment and maybe not looking at the right places.

I use a server where I already have some docker containers running, for that I use nginxproxy/nginx-proxy docker container as reverse proxy. I looked at the api-platofrm documentation on how to deploy with docker-compose : https://api-platform.com/docs/deployment/docker-compose/#deploying but since I'm working on this subject I evolve between "502 bad gateway" or "The page is not redirected correctly" errors.

Actually I've got this docker-compose.yml :

version: "3.4"

services:
  php:
    build:
      context: ./api
      target: api_platform_php
    depends_on:
      - database
    restart: unless-stopped
    volumes:
      - php_socket:/var/run/php
    healthcheck:
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 30s
    networks:
      - 'cloud'

  caddy:
    build:
      context: api/
      target: api_platform_caddy
    depends_on:
      - php
    environment:
      PWA_UPSTREAM: pwa:3000
      SERVER_NAME: ${SERVER_NAME:-localhost, caddy:80}
      MERCURE_PUBLISHER_JWT_KEY: ${MERCURE_PUBLISHER_JWT_KEY:-!ChangeMe!}
      MERCURE_SUBSCRIBER_JWT_KEY: ${MERCURE_SUBSCRIBER_JWT_KEY:-!ChangeMe!}
    restart: unless-stopped
    volumes:
      - php_socket:/var/run/php
      - caddy_data:/data
      - caddy_config:/config
    ports:
      # HTTP
      - target: 80
        published: 7000
        protocol: tcp
      # HTTPS
      - target: 443
        published: 7001
        protocol: tcp
      # HTTP/3
      - target: 443
        published: 7001
        protocol: udp
    networks:
      - 'cloud'

  database:
    image: postgres:13-alpine
    environment:
      - POSTGRES_DB=api
      - POSTGRES_PASSWORD=!ChangeMe!
      - POSTGRES_USER=api-platform
    volumes:
      - db_data:/var/lib/postgresql/data:rw
      # you may use a bind-mounted host directory instead, so that it is harder to accidentally remove the volume and lose all your data!
      # - ./api/docker/db/data:/var/lib/postgresql/data:rw
    networks:
      - 'cloud'

volumes:
  php_socket:
  db_data:
  caddy_data:
  caddy_config:


networks:
  cloud:
    external: true

and this docker-compose.preprod.yml file :

version: "3.4"

# Preproduction environment override
services:
  php:
    environment:
      APP_ENV: prod
      APP_SECRET: ${APP_SECRET}

  caddy:
    environment:
      MERCURE_PUBLISHER_JWT_KEY: ${MERCURE_PUBLISHER_JWT_KEY:-!ChangeMe!}
      MERCURE_SUBSCRIBER_JWT_KEY: ${MERCURE_SUBSCRIBER_JWT_KEY:-!ChangeMe!}
      VIRTUAL_HOST: api-preprod.melofeel.com
      VIRTUAL_PORT: 80
      LETSENCRYPT_HOST: api-preprod.melofeel.com

I'm deploying it with gitlab-ci and launching it with this command : SERVER_NAME=******.*****.com APP_SECRET=testdeploy POSTGRES_PASSWORD=testdeploy CADDY_MERCURE_JWT_SECRET=testdeploy docker-compose -f api_preprod/docker-compose.yml -f api_preprod/docker-compose.preprod.yml up -d

I've tried to run it with and without Caddy, without I always get "502 bad gateway".

The 3 containers are running, but when I look on Caddy logs I've got this messages :

{"level":"error","ts":1648201680.3190682,"logger":"tls.issuance.acme.acme_client","msg":"challenge failed","identifier":"*****.*****.com","challenge_type":"http-01","problem":{"type":"urn:ietf:params:acme:error:unauthorized","title":"","detail":"Invalid response from http://*****.*****.com/.well-known/acme-challenge/O9zJRdytI8vlf7yZLRcV9pzUlmI73ysCqQJTHg8XWTw [188.165.218.39]: 404","instance":"","subproblems":[]}}

I've tried to deactivate the automatic https from caddy, nginx-proxy is already responsible for doing it, but it's seems to not work. My Caddyfile :

{
    # Debug
    {$DEBUG}
    # HTTP/3 support
    servers {
        protocol {
            experimental_http3
        },
        auto_https disable_redirects
    }
}

{$SERVER_NAME}

log

# Matches requests for HTML documents, for static files and for Next.js files,
# except for known API paths and paths with extensions handled by API Platform
@pwa expression `(
        {header.Accept}.matches("\\btext/html\\b")
        && !{path}.matches("(?i)(?:^/docs|^/graphql|^/bundles/|^/_profiler|^/_wdt|\\.(?:json|html$|csv$|ya?ml$|xml$))")
    )
    || {path} == "/favicon.ico"
    || {path} == "/manifest.json"
    || {path} == "/robots.txt"
    || {path}.startsWith("/_next")
    || {path}.startsWith("/sitemap")`

route {
    root * /srv/api/public
    mercure {
        # Transport to use (default to Bolt)
        transport_url {$MERCURE_TRANSPORT_URL:bolt:///data/mercure.db}
        # Publisher JWT key
        publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY} {env.MERCURE_PUBLISHER_JWT_ALG}
        # Subscriber JWT key
        subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY} {env.MERCURE_SUBSCRIBER_JWT_ALG}
        # Allow anonymous subscribers (double-check that it's what you want)
        anonymous
        # Enable the subscription API (double-check that it's what you want)
        subscriptions
        # Extra directives
        {$MERCURE_EXTRA_DIRECTIVES}
    }
    vulcain
    push

    # Add links to the API docs and to the Mercure Hub if not set explicitly (e.g. the PWA)
    header ?Link `</docs.jsonld>; rel="http://www.w3.org/ns/hydra/core#apiDocumentation", </.well-known/mercure>; rel="mercure"`

    # Comment the following line if you don't want Next.js to catch requests for HTML documents.
    # In this case, they will be handled by the PHP app.
    reverse_proxy @pwa http://{$PWA_UPSTREAM}

    php_fastcgi unix//var/run/php/php-fpm.sock
    encode zstd gzip
    file_server
}

Thanks in advance for any help and explanation that allow me to understand what the problem is.

1 Answers

I managed to get it working by changing the caddy server name to

  SERVER_NAME: ${SERVER_NAME:-localhost, caddy}:80

Then in Nginx Proxy redirect it to the IP over http to port 80 with a HTTPS certificate etc but I've found this breaks Vulcain for preloading fetch requests with the following error

A preload for '<URL>' is found, but is not used because the request credentials mode does not match. Consider taking a look at crossorigin attribute.

I haven't manage to fix it yet and can confirm my setup works when I point my DNS record directly to the Server running the docker as opposed to Nginx-Proxy

Related