5.7 KiB
WARNING: This document describes only the change applied in this PR. It may already be outdated once the next PR is merged. Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation.
The Problem
instance-update restarts the systemd unit: for a few seconds nothing listens on Werkator's port at all.
On the Hostsharing Managed Webspace deployment (see deployment.md) the platform's Apache sits in front and proxies via .htaccess (SystemdServiceFiles.htaccessContent); with the backend refusing connections, Apache answers with its own bare 502 error page, or the request just hangs until it times out.
Either way the instance looks dead rather than "back in a moment", which is confusing during a deploy that is otherwise routine and fast (the restart itself is under a second; the rest of instance-update's time is upload).
Non-Goals
- A live progress indicator ("2 of 10 seconds left") — the maintenance page is a static file with no way to know how far the restart has gotten.
- Zero-downtime / blue-green deployment — the restart gap itself is not eliminated, only made to look intentional instead of broken.
- The Docker-host and bwrap-webspace-without-a-domain deployment variants, which do not go through Apache/
.htaccessat all.
The Scenarios
Feature: a refused connection shows a maintenance page instead of a bare error
Background
init --systemdwrites the.htaccessonly whenserver.publicBaseUrlis set — unchanged; the maintenance page is generated under the same condition, next to it.- The generated
.htaccessalready proxies every request tohttp://127.0.0.1:<port>viamod_rewrite [P]; a refused connection surfaces as Apache's own 502 (and, depending on Apache's timeout handling, 503/504).
Scenario#17.01: A refused backend connection serves the maintenance page
So that a deployment's restart window reads "please retry" instead of a bare error or a hang.
- Given the generated
.htaccessandwerkator-maintenance.htmlare in a domain's docroot - When the proxied backend refuses the connection (Apache's 502/503/504)
- Then Apache serves
werkator-maintenance.htmlinstead of its default error page
Verified by
- SystemdServiceFilesTest — "the htaccess maps a refused connection to the static maintenance page"
- Manual, pending: an
instance-updatedeploy onmih09watched live during the restart window, to confirm Apache actually reaches 502 there rather than timing out first (see Open Questions).
Scenario#17.02: The maintenance page itself is never proxied
So that ErrorDocument's internal sub-request for the page does not loop back into the same rewrite rule (which would proxy it to the — still down — backend and fail again).
- Given the generated
.htaccess - When Apache serves
werkator-maintenance.htmlas anErrorDocument - Then the
RewriteCondexcludes exactly that path from the proxyRewriteRule
Verified by
Scenario#17.03: A host without a configured public base URL is unaffected
So that a developer machine or a Docker-host deployment, which never went through .htaccess, sees no new file and no behavior change.
- Given
server.publicBaseUrlis blank - When
init --systemdruns - Then neither
.htaccessnorwerkator-maintenance.htmlis written — unchanged from before this PR
Verified by
- Existing
InitCommandbehavior (server.publicBaseUrl.isNotBlank()gate); not separately tested, as before this PR.
The Solution
SystemdServiceFiles.htaccessContent gains three ErrorDocument lines (502/503/504) pointing at a new static werkator-maintenance.html, plus one RewriteCond excluding that file from the catch-all proxy rule.
SystemdServiceFiles.maintenancePageContent() is a small, self-contained HTML page ("Werkator is restarting — please retry in a few minutes") — no external CSS/fonts/images, since nothing would be there to serve them while Werkator itself is the thing that is down.
InitCommand.createSystemdFiles writes it under the same server.publicBaseUrl.isNotBlank() gate as the .htaccess, next to it in .git/werkator/.
tools/remote instance-start copies both files into the domain docroot in the same step (it already copied the .htaccess there; the maintenance page rides along).
No config key was added: like the .htaccess itself, the maintenance page is generated whenever a publicBaseUrl is configured, not behind a separate toggle nobody is expected to turn off.
Open Questions
- Does Apache actually reach 502, or does the client just time out first? Depends on Apache's
ProxyTimeout/connect-timeout configuration on the managed webspace, which this PR does not change or configure — where Apache's own request timeout fires before the backend's refused-connection error would, the client still sees a hang, not the maintenance page, for that fraction of the restart window. Not addressed here; a follow-up could set an explicit shortProxyTimeoutif this turns out to matter in practice.
Additional Changes
- None beyond the feature itself.
Prerequisite PRs
- None; builds on the existing
.htaccessgeneration (SystemdServiceFiles,InitCommand), unchanged in shape.
Follow-up PRs
- An explicit Apache
ProxyTimeoutif the client-side hang (see Open Questions) turns out to matter in practice.