Skip to content

Configuration

Everything is configured with environment variables, or with a YAML file named by CONFIG_FILE. Environment variables win over the file, and both win over the defaults. A variable that is set but empty counts as unset. The full list is in Environment variables.

Invalid configuration (an unknown theme or language, random mixed with theme names, a malformed LINKS value, a bad URL) stops the container at start with a one-line message instead of rendering broken pages. Check docker logs if it does not come up.

Mount a file and point CONFIG_FILE at it:

/config/config.yaml
theme: status # or a list such as "glass,status", or random
languages: [en, de]
default_language: en
default_page: "404"
send_same_http_code: true
show_details: true
show_footer: true
show_version: false
copyright_name: Example Inc.
copyright_url: https://example.com
links:
- id: imprint
urls:
de: https://example.com/de/impressum
en: https://example.com/en/imprint
labels:
de: Impressum
en: Imprint
- id: privacy
url: https://example.com/privacy
labels:
de: Datenschutz
en: Privacy
services:
error-pages:
image: noixdev/error-pages
environment:
CONFIG_FILE: /config/config.yaml
volumes:
- ./config.yaml:/config/config.yaml:ro

The keys are the lower-case names of the environment variables, for example default_language for DEFAULT_LANGUAGE. Unknown keys are rejected, so a typo is caught at start. Quote default_page ("404") so that YAML reads it as text.

Links appear in a menu on the page. Each link has:

FieldMeaning
idRequired. Unique name of the link; also the fallback label.
urlOne address for every language.
urlsOne address per language, for example de and en. Wins over url.
labelsThe text per language. A missing label falls back to the DEFAULT_LANGUAGE label, then to the id.

A link needs url, or urls. If you give only urls, it must cover every language in LANGUAGES. Links open in the same tab, and an empty list shows no menu.

Addresses must start with http:// or https://, or be a path on the same site that starts with a single /. Anything else (such as javascript: or //host) is rejected, as are addresses containing spaces or control characters. The same rules apply to COPYRIGHT_URL.

In the environment, LINKS is a JSON array with the same fields:

Terminal window
LINKS='[{"id":"imprint","url":"https://example.com/imprint","labels":{"en":"Imprint","de":"Impressum"}}]'
  • COPYRIGHT_NAME shows ”© year name” in the footer; COPYRIGHT_URL makes the name a link.
  • SHOW_VERSION adds the version, and SHOW_FOOTER=false hides the footer.
  • SHOW_DETAILS shows the time (UTC), host and request ID. Only the status theme displays them. The host comes from X-Forwarded-Host, else Host, and the request ID from X-Request-Id, else a random one per request.

By default a page is answered with its own status code, so bots and monitors see the truth: the page for 502 comes with status 502. Set SEND_SAME_HTTP_CODE=false to answer 200 for every page.

The page is chosen in this order:

  1. The request path: /404, /404.html, /internal, /maintenance.
  2. An X-Code request header with a code from 400 to 599, if the path names no page.
  3. DEFAULT_PAGE for any other path.

A path that names a page therefore always wins, so a client cannot change the page a proxy rewrote its request to by sending its own X-Code.

Clients that ask for JSON get {"code":503,"title":"...","description":"..."} and clients that ask for plain text get 503 Title, in the detected language. The Accept header decides, with its quality values: the format with the highest q wins, a tie or an Accept header without a usable format gives the HTML page, and q=0 rules a format out. application/json, text/html;q=0 therefore gets JSON.

Maintenance mode replaces every genuine server error page (5xx) with the maintenance page, answered with status 503. Client errors such as 404 are unaffected. It is on when either of these is true:

  • the flag file exists (default /data/maintenance, set with MAINTENANCE_FILE), or
  • MAINTENANCE=true is set.

Use the command line inside the running container. No restart is needed; the server looks at the flag file about once per second.

Terminal window
docker exec error-pages /error-pages maintenance on
docker exec error-pages /error-pages maintenance on --until 2026-12-01T18:00:00Z
docker exec error-pages /error-pages maintenance status
docker exec error-pages /error-pages maintenance off

status prints off, on or on until <time>. The command writes the flag file, so the directory (/data) must exist and be writable. Mount a volume on /data if the switch should survive the container being recreated:

Terminal window
docker run --name error-pages -v error-pages-data:/data noixdev/error-pages

With an end time, visitors see when you expect to be back and the response carries a Retry-After header. The time is RFC 3339, for example 2026-12-01T18:00:00Z.

  • maintenance on --until <time> stores the time in the flag file.
  • MAINTENANCE_UNTIL sets it for maintenance switched on with MAINTENANCE=true, or for a flag file that holds no time. A time in the flag file wins.
  • Once the time has passed, the end time is no longer shown or sent, but the maintenance page stays until you switch maintenance off.

Set MAINTENANCE=true (optionally with MAINTENANCE_UNTIL) to start a container that way. Switching it off with maintenance off removes the flag file only, so a container started with MAINTENANCE=true stays in maintenance mode until you restart it without the variable.