I did this config on my previous server, and honestly don't recall exactly what my pagespeed score was before the changes but I want to say it was in the mid 60% range.

There is a simple change that can be made when running Ghost in docker, to make it perform better for the end user – chuck it behind lightify, which will automagically apply a bunch of best practices - concatenation/compression of JS, CSS, etc for you. This lets you go on about the business of blogging without worrying about how to reconfigure things to eek out better pagespeed performance.

Now as you are probably already aware, I run all my various services in docker, proxied behind traefik. It's working fabulously and I have a bit better understanding of it after my recent efforts to move to a new server and reconfigure all the things.

Anyway, I'll keep it short and to the point. Here is basically what my docker-compose file looks lik for this ghost blog:

version: "3"
networks:
  proxy:
    external: true
services:
  ghost:
    image: ghost:3-alpine
    container_name: ghost
    restart: unless-stopped
    domainname: example.com
    expose:
      - "2368"
    networks:
      - proxy
    volumes:
      - ./config/config.production.json:/var/lib/ghost/config.production.json:ro
      - ./data:/var/lib/ghost/content
  lightify:
    image: alash3al/lightify
    entrypoint: ["lightify", "-http", ":8880", "--upstream=http://ghost:2368"]
    restart: unless-stopped
    expose:
      - 8880
    networks:
      - proxy
    labels:
      - traefik.enable=true
      - traefik.docker.network=proxy
      # Next four lines handle redirect from www to non-www version of url:
      - traefik.http.middlewares.www-redirect.redirectregex.regex=^https://www.example.com/(.*)
      - traefik.http.middlewares.www-redirect.redirectregex.replacement=https://example/$${1}
      - traefik.http.middlewares.www-redirect.redirectregex.permanent=true
      - traefik.http.routers.lightify.middlewares=www-redirect
      - traefik.http.routers.lightify.entrypoints=https
      - traefik.http.routers.lightify.rule=Host(`example.com`)
      - traefik.http.routers.lightify.tls=true
      - traefik.http.routers.lightify.tls.certresolver=default
      - traefik.http.routers.lightify.tls.domains[0].main=example.com
      - traefik.http.routers.lightify.tls.domains[0].sans=*.example.com

That's basically it. Traefik points the desired host, in this case example.com at lightify, which in turn is a reverse proxy for the ghost container.

Additional nicities in this config, it will redirect from the www to non-www version of the domain.  I also have automatic redirect from http to https setup globally within my traefik's docker-compose file, which I covered in my post yesterday about setting up Mailcow behind traefik, but for brevity here's the relevant labels for that too:

services:
  traefik:
  
    # --- snipping out all the other stuff --
    
    labels:
      # Global Redirect to https
      - traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)
      - traefik.http.routers.http-catchall.entrypoints=http
      - traefik.http.routers.http-catchall.middlewares=redirect-to-https
      - traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https

That catches incoming http requests, for any host, and redirects them to https instead. Since traefik is grabbing certs for all the things anyway, this works nicely. Caveat– I imagine it would not work so nice if you were dependent on the http challenge type for getting letsencrypt certs, so best to use the dns challenge and/or tls challenge.

There is one small gotcha I've discovered with lightify, for which I have opened an issue on github, if using <link rel="... to load google web fonts, lightify is mangling the urls. The workaround is to instead direcly embed the stylesheets that get loaded.  See the github issue for full details.