Api-platform/Caddy/Varnish

1. Caddy version (caddy version):

Stack Api-platform with Caddy 2

2. How I run Caddy:

I have several domain with api platform for backend and front end with React/Next-js

a. System environment:

Docker environment

b. Command:

docker-compose up -d

c. Service/unit/compose file:

version: "3.4"

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

  pwa:
    build:
      context: ./pwa
      target: api_platform_pwa_prod
    environment:
      API_PLATFORM_CLIENT_GENERATOR_ENTRYPOINT: http://caddy
      NEXT_PUBLIC_ENTRYPOINT: http://caddy
    volumes:
      - public:/usr/src/pwa/public/static:rw

  caddy:
    build:
      context: api/
      target: api_platform_caddy
    depends_on:
      - php
      - pwa
    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

  varnish:
    image: varnish:stable
    restart: unless-stopped
    tmpfs: '/var/lib/varnish:exec'
    ports:
      - '8080:80'
    volumes:
      - ./data/varnish/default.vcl:/etc/varnish/default.vcl:ro
    depends_on:
      - caddy

  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

volumes:
  php_socket:
  db_data:
  caddy_data:
  caddy_config:
  public:
    external: true

d. My complete Caddyfile or JSON config:

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

{$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("/static/")
    || {path}.startsWith("/fonts/")
    || {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"`
    # Disable Google FLOC tracking if not enabled explicitly: https://plausible.io/blog/google-floc
    header ?Permissions-Policy "interest-cohort=()"

    # 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
}

3. The problem I’m having:

I’m trying to use Varnish with Caddy and Api-platform, but don’t find any complete docs for this implementation. My Varnish service start right, but I don’t know how plug it with Caddy correctly.

My varnish configuration (for beginning):

vcl 4.0;

backend default {
    .host = "caddy:80";
}

4. Error messages and/or full log output:

5. What I already tried:

I’ll try make Varnish depend on Caddy, Caddy depend on Varnish… Same result: nothing.
I’ll try to pass Varnish as a proxy, but ‘pwa’ is already define as a reverse_proxy and don’t run correctly when change to Varnish.
I’m newbee in this part of configuration, and I am aware that I don’t really know what I am doing.

6. Links to relevant resources:

I’m not really sure I understand what you’re trying to do here.

What’s your goal in using Varnish exactly? What are you trying to cache?

This question is probably better asked of the api-platform team, since they designed this architecture.

Thanks for your reply @francislavoie

I ask the same question on Symfonydev slack in channel api-platform, but no answers at this time.

As I think understand, I have to configure a proxy for Varnish, but I don’t find any docs about api-platform specificities.

Well, running Varnish in front of Caddy is somewhat more complicated, because of TLS.

Caddy automates your certificate management, so it wants to be the first thing that gets hit by ACME CAs, to solve the challenges, etc.

If Varnish was in front, then it would need to terminate TLS to be able to act on behalf of your server.

Not sure if helpful, but did you know Caddy can act as a cache also? GitHub - caddyserver/cache-handler: Distributed HTTP caching module for Caddy – it is being designed as an alternative to Varnish or other caches so you can simplify your stack and config.

Thanks for your reply, @matt

I know this plugin. I find my issue with the push directive.

At first I have a problem with the front part who call api-platform with pending requests indefinitely. I thought that adding Varnish could solve the problem. But after searches I found soluce with disabling push directive.

My issue is solve for now. I know that plugin is still in dev state, but I am following the evolution to use it later.

1 Like

Unfortunately, the cache module still lacks some main features of Varnish (invalidation especially). Proposal to use Souin as cache system by darkweak · Pull Request #17 · caddyserver/cache-handler · GitHub should help a lot to fill the gap.

API Platform doesn’t provide Varnish with the distribution right now, but it should be possible to do something like that using a single instance of Caddy:

Caddy (TLS) → Varnish → Caddy (virtual host listening on the local network only) → PHP

It would be nice to try (and document) that!

2 Likes

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