Skip to content

Etherpad does not start behind corporate firewall. #7911

Closed

Description

@frwa-enpace

Describe the bug
Etherpad cannot start in offline or air-gapped environments (e.g. behind a corporate firewall). Two root causes:

  1. 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.
  2. 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

  1. Deploy Etherpad in a Docker container with no outbound internet access (or behind a corporate firewall blocking package registry traffic).
  2. Start the container.
  3. 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.

Activity

  1. JohnMcLear commented on Jun 8, 2026

    @JohnMcLear
    Member

    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:

    "privacy": { "updateCheck": false, "pluginCatalog": false },
    "updates": { "tier": "off" }

    If you can confirm the deployment type for issue #1, we'll get the env-var mapping sorted for #2. Thanks again!

  2. JohnMcLear commented on Jun 8, 2026

    @JohnMcLear
    Member

    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.

  3. frwa-enpace commented on Jun 9, 2026

    @frwa-enpace
    Author

    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.

    Thank you,

    Frank

  4. JohnMcLear commented on Jun 9, 2026

    @JohnMcLear
    Member

    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.

    Fix is up as #7918:

    • 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions