You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Etherpad does not start behind corporate firewall. #7911
Describe the bug
Etherpad cannot start in offline or air-gapped environments (e.g. behind a corporate firewall). Two root causes:
The packageManager field in package.json causes the package manager (via corepack) to attempt a network call to verify/update itself on startup. Without internet access, this fails and the container never starts.
The settings privacy.updateCheck, privacy.pluginCatalog, and updates.* in settings.json cannot be overridden via environment variables, making it impossible to disable outbound network calls without modifying settings.json inside the container image.
To Reproduce
Deploy Etherpad in a Docker container with no outbound internet access (or behind a corporate firewall blocking package registry traffic).
Start the container.
Observe startup failure caused by the packageManager resolution/update step.
To hit issue #2: attempt to disable update checks and plugin catalog lookups purely via environment variables — no documented mapping exists.
Expected behavior
Etherpad should start successfully in offline/air-gapped environments without requiring internet access at startup.
The settings privacy.updateCheck, privacy.pluginCatalog, and updates.* should be configurable via environment variables (e.g. ETHERPAD_PRIVACY_UPDATE_CHECK=false) so operators can disable outbound calls without modifying files inside the container image.
Proposed fixes
Remove (or make optional) the packageManager entry in package.json to prevent forced network calls at startup.
Add environment-variable mappings for privacy.updateCheck, privacy.pluginCatalog, and updates.*.
Screenshots
N/A
Server (please complete the following information):
Etherpad version: 3.2.0
OS: RHEL 9
Desktop (please complete the following information):
N/A — issue occurs at server startup before any client interaction.
Smartphone (please complete the following information):
N/A
Additional context
This is a common pain point for enterprise and self-hosted deployments where security policies prohibit outbound connections from application containers. Both fixes are low-risk and would significantly improve offline/air-gapped deployment support.
Thanks for the detailed write-up, @frwa-enpace — air-gapped/firewalled deployment is a real use case and the report is appreciated. A few notes after checking this against the current develop branch:
1. packageManager / corepack at startup
The official Docker image on develop no longer uses corepack at all. pnpm is installed directly via npm install -g pnpm (corepack was explicitly dropped because Node 25 removed it from the distribution — see the comment in Dockerfile), and the runtime entrypoint runs node directly (node --require tsx/cjs node/server.ts), not pnpm run prod. So when the official container boots, no package manager — and no corepack — is invoked, and the packageManager field cannot trigger a network call at startup.
The one scenario where the packageManager field can cause corepack to reach out is a source/host install on a machine with corepack enable active (e.g. running pnpm run prod directly). Since you're on 3.2.0, could you confirm whether you're hitting this with the official Docker image or a source-based install? That tells us whether this reproduces on current develop at all.
Also worth noting: the hourly update check is already non-blocking and short-circuits on privacy.updateCheck=false — the fetch is fire-and-forget and wrapped in a .catch, so a blocked updateServer logs a caught error rather than preventing boot.
2. Env-var overrides for privacy.* and updates.*
This one is a fair point about the default experience. Etherpad already does universal ${VAR} / ${VAR:default} substitution across the entire settings object, with boolean coercion — so privacy.updateCheck, privacy.pluginCatalog, and the whole updates.* block can be made env-driven today. The gap is that the shipped settings.json.template hardcodes literal values, so out of the box there's no ready-made ${ETHERPAD_...} placeholder and you'd have to edit the file.
So the right fix here is a template + docs change — wire ${...} placeholders into those keys and document the env names — rather than removing packageManager. That's low-risk and we're happy to take it on.
In the meantime, you can already disable the outbound calls without code changes by setting in your settings.json:
For item 2, the env-var mapping is now up as #7917 — it wires PRIVACY_UPDATE_CHECK, PRIVACY_PLUGIN_CATALOG, UPDATES_TIER (set to off for fully offline), and a few related UPDATES_* / UPDATE_SERVER knobs through the existing ${ENV:default} substitution in both settings.json.docker and settings.json.template, with docs and backend regression tests. No code changes — the substitution engine already supported this; the shipped Docker settings file just didn't expose these keys.
Still holding on item 1 (the packageManager/corepack startup claim) pending your confirmation of whether you hit it on the official Docker image or a source/host install — on current develop the official image installs pnpm via npm (corepack was dropped for Node 25) and starts node directly, so there's no package-manager call at container startup to fail.
Still holding on item 1 (the packageManager/corepack startup claim) pending your confirmation of whether you hit it on the official Docker image or a source/host install — on current develop the official image installs pnpm via npm (corepack was dropped for Node 25) and starts node directly, so there's no package-manager call at container startup to fail.
Hi @JohnMcLear - thanks a lot. Yes, I am hitting this on the official docker image. I had to remove the packageManager entry to make it work. I am using node directly as an entrypoint.
pnpm is invoked even though updates are disabled, and it throws an error:
[WARN] plugins - Failed to get pnpm version: Error: Command exited with code 1: pnpm --version
You can easily verify this if you run pnpm with any parameter on the docker image without access to the internet.
Thanks @frwa-enpace — that confirmation cracked it, and you were right. My earlier read was incomplete: I said "no package manager runs at startup," but the pnpm --version probe Etherpad logs at boot is the trigger.
Root cause: the official image installs pnpm directly via npm (corepack was dropped for Node 25+), but standalone pnpm still honours the packageManager pin in package.json through its own version management (the pnpm 11 pmOnFail setting). The image shipped pnpm 11.0.6 against a pnpm@11.1.2 pin, so every pnpm call tried to download 11.1.2 — which fails with no network, exactly your Command exited with code 1: pnpm --version. So it's pnpm self-provisioning, not corepack — and removing packageManager worked for you because it removes the thing pnpm tries to fetch.
Realign the image's pnpm to the pin (no mismatch → no download), and set pnpm_config_pm_on_fail=ignore in the Dockerfile so any future drift or other offline pnpm call uses the installed pnpm instead of the network.
The startup probe + the updater's pnpm checks now run with that same flag, so non-Docker offline installs are covered too and the probe can never fail-loud.
A backend test fails CI if the Dockerfile pnpm version ever drifts from the packageManager pin again.
I verified with a standalone (non-corepack) pnpm: a pin mismatch makes pnpm --version exit 1 by default, and exit 0 reading the local version with pm_on_fail=ignore.
Between #7917 (item 2 — env-var overrides) and #7918 (item 1 — this), both of your points are addressed. Thanks again for the precise repro.
Activity
JohnMcLear commentedon Jun 8, 2026
Thanks for the detailed write-up, @frwa-enpace — air-gapped/firewalled deployment is a real use case and the report is appreciated. A few notes after checking this against the current
developbranch:1.
packageManager/ corepack at startupThe official Docker image on
developno longer uses corepack at all. pnpm is installed directly vianpm install -g pnpm(corepack was explicitly dropped because Node 25 removed it from the distribution — see the comment inDockerfile), and the runtime entrypoint runsnodedirectly (node --require tsx/cjs node/server.ts), notpnpm run prod. So when the official container boots, no package manager — and no corepack — is invoked, and thepackageManagerfield cannot trigger a network call at startup.The one scenario where the
packageManagerfield can cause corepack to reach out is a source/host install on a machine withcorepack enableactive (e.g. runningpnpm run proddirectly). Since you're on 3.2.0, could you confirm whether you're hitting this with the official Docker image or a source-based install? That tells us whether this reproduces on currentdevelopat all.Also worth noting: the hourly update check is already non-blocking and short-circuits on
privacy.updateCheck=false— the fetch is fire-and-forget and wrapped in a.catch, so a blockedupdateServerlogs a caught error rather than preventing boot.2. Env-var overrides for
privacy.*andupdates.*This one is a fair point about the default experience. Etherpad already does universal
${VAR}/${VAR:default}substitution across the entire settings object, with boolean coercion — soprivacy.updateCheck,privacy.pluginCatalog, and the wholeupdates.*block can be made env-driven today. The gap is that the shippedsettings.json.templatehardcodes literal values, so out of the box there's no ready-made${ETHERPAD_...}placeholder and you'd have to edit the file.So the right fix here is a template + docs change — wire
${...}placeholders into those keys and document the env names — rather than removingpackageManager. That's low-risk and we're happy to take it on.In the meantime, you can already disable the outbound calls without code changes by setting in your
settings.json:If you can confirm the deployment type for issue #1, we'll get the env-var mapping sorted for #2. Thanks again!
JohnMcLear commentedon Jun 8, 2026
For item 2, the env-var mapping is now up as #7917 — it wires
PRIVACY_UPDATE_CHECK,PRIVACY_PLUGIN_CATALOG,UPDATES_TIER(set toofffor fully offline), and a few relatedUPDATES_*/UPDATE_SERVERknobs through the existing${ENV:default}substitution in bothsettings.json.dockerandsettings.json.template, with docs and backend regression tests. No code changes — the substitution engine already supported this; the shipped Docker settings file just didn't expose these keys.Still holding on item 1 (the
packageManager/corepack startup claim) pending your confirmation of whether you hit it on the official Docker image or a source/host install — on currentdevelopthe official image installs pnpm via npm (corepack was dropped for Node 25) and startsnodedirectly, so there's no package-manager call at container startup to fail.frwa-enpace commentedon Jun 9, 2026
Hi @JohnMcLear - thanks a lot. Yes, I am hitting this on the official docker image. I had to remove the
packageManagerentry to make it work. I am usingnodedirectly as an entrypoint.pnpmis invoked even though updates are disabled, and it throws an error:[WARN] plugins - Failed to get pnpm version: Error: Command exited with code 1: pnpm --versionYou can easily verify this if you run
pnpmwith any parameter on the docker image without access to the internet.Thank you,
Frank
JohnMcLear commentedon Jun 9, 2026
Thanks @frwa-enpace — that confirmation cracked it, and you were right. My earlier read was incomplete: I said "no package manager runs at startup," but the
pnpm --versionprobe Etherpad logs at boot is the trigger.Root cause: the official image installs pnpm directly via npm (corepack was dropped for Node 25+), but standalone pnpm still honours the
packageManagerpin in package.json through its own version management (the pnpm 11pmOnFailsetting). The image shipped pnpm 11.0.6 against apnpm@11.1.2pin, so every pnpm call tried to download 11.1.2 — which fails with no network, exactly yourCommand exited with code 1: pnpm --version. So it's pnpm self-provisioning, not corepack — and removingpackageManagerworked for you because it removes the thing pnpm tries to fetch.Fix is up as #7918:
pnpm_config_pm_on_fail=ignorein the Dockerfile so any future drift or other offline pnpm call uses the installed pnpm instead of the network.packageManagerpin again.I verified with a standalone (non-corepack) pnpm: a pin mismatch makes
pnpm --versionexit 1 by default, and exit 0 reading the local version withpm_on_fail=ignore.Between #7917 (item 2 — env-var overrides) and #7918 (item 1 — this), both of your points are addressed. Thanks again for the precise repro.
fix(docker): don't let pnpm self-provision a pinned version on offlin…
Fix offline/air-gapped Docker boot: stop pnpm self-provisioning the p…
docs(changelog): fold post-3.3.0-draft commits into the 3.3.0 notes (#…
Env-var overrides for offline/air-gapped installs (update check, plug…