#1934·kamal

`kamal setup` fails on a fresh server when an accessory has a `proxy` block and `error_pages_path` is set

Author: am1006Created Aug 22, 2026Updated Aug 25, 2026

First: thank you for Kamal. We run our whole product on it and it has been a joy. We hit one ordering issue during a server migration and we want to report it clearly. We may be missing something - if so, we are happy to learn the intended flow.

Summary

On a brand-new server, kamal setup fails before the app boots when the config has both:

  • error_pages_path set, and
  • an accessory with a proxy section.

The accessory registration fails with:

Error: unable to load error pages

Reproduction

Kamal 2.12.0. A minimal config shape:

yaml
error_pages_path: public

accessories:
  streams:
    image: alpine:latest
    host: 10.0.0.1
    proxy:
      host: app.example.com
      path_prefix: "/v1/streams"
      app_port: 4437

Run kamal setup against a server that has never had a deploy. Setup fails at accessory:boot.

What we think happens

In Kamal::Cli::Main#deploy (with boot_accessories: true, which setup uses), the order is:

  1. proxy:boot
  2. accessory:boot all
  3. app:boot

The error pages upload to the proxy directory during step 3 (cli/app/error_pages.rb). But step 2 already passes --error-pages=<dir>/<version> when it registers the accessory with kamal-proxy, and kamal-proxy validates that the directory exists at registration time. On a fresh server the directory does not exist yet, so step 2 fails and setup stops. Step 3 never runs.

The failure needs all three conditions at once: a proxied accessory, a set error_pages_path, and a server with no prior deploy. A server that had any earlier deploy has the directory, so the problem hides on existing machines and appears only on new ones.

A second, smaller interaction: kamal app boot prunes the error-pages directories of other versions. app boot resolves its version from the deployed marker while deploy and accessory commands resolve it from git. When these differ, a later accessory boot/start can reference a directory that the prune just removed, and the same error returns on a server that has deployed fine.

Workaround we use

We replaced kamal setup on fresh servers with an explicit order:

kamal server bootstrap
kamal accessory boot <each accessory without a proxy block>
kamal deploy
kamal accessory boot <each accessory with a proxy block>

This works reliably.

Possible directions

We do not know the internals well enough to say which fix fits best. Two ideas, in case they help:

  • boot accessories with a proxy section after app:boot inside setup, or
  • let the accessory registration tolerate a missing error-pages directory and fall back to default error pages.

Thanks again for the tool and for reading this far.