IPv6 environments

Environments can opt into IPv6 on their Docker network via the create-time IPV6_ENABLED parameter (DEV-2590). This page covers the opt-in model, the agent onboarding required, and the security implications.

Opt-in model (create-time only)

  • IPV6_ENABLED is a Create-Environment parameter, default off. It is stored once in meta.json as ipv6_enabled (boolean) and is immutable for the env’s life — Update-Environment has no IPv6 parameter by design.

  • To change IPv6 on an existing env, clone or re-create it with the box ticked. CLONE_FROM reproduces the services / DB / versions / variables; it does not copy ipv6_enabled, so you set it explicitly on the new env.

  • Rationale for create-only: it removes the two failure modes a mutable toggle would introduce — a static checkbox re-defaulting to unticked on every unrelated Update build (silently dropping a saved IPv6 setting), and the Docker "network needs to be recreated" error that changing enable_ipv6 on an already-deployed network throws (deploy runs docker compose up with no preceding down). A create-only, immutable flag makes the injected network block deterministic across every redeploy.

meta.ipv6_enabled is not a supported manual-edit surface. Hand-editing it on a deployed env re-introduces the network-recreate error on the next compose up.

What happens on deploy

When ipv6_enabled is true, deployEnvStep:

  1. Runs an IPv6 preflight on the chosen DEPLOY_RUNNER (fail-closed — any failing or inconclusive check aborts the deploy before any container starts):

    1. kernel IPv6 enabled (net.ipv6.conf.all.disable_ipv6 == 0);

    2. operator attestation — the node-scoped Node Property QA_AGENT_IPV6_READY == true (read via the Jenkins node API, never the pipeline env. global, so a global value cannot attest all agents at once);

    3. a subnet-less docker network create --ipv6 capability probe (exercises the same daemon default-address-pools path compose up uses), cleaned up immediately;

    4. best-effort removal of a stale same-name <env>_default network only when no containers are attached.

  2. Overrides the compose default network with enable_ipv6: true (no explicit subnet — the daemon auto-assigns a unique /64 per env). Services are not given per-service networks: keys; they stay on default, which now carries IPv6.

Legacy fixed-port envs (legacy_ports=true, pre-Phase-3) cannot use IPv6: the deploy fails fast if the combination is ever reached (it is unreachable via the create-only flag, since new envs are always legacy_ports=false).

Agent onboarding checklist

Perform on each agent that should serve IPv6 envs, then set the attestation:

  1. Enable IPv6 in the host kernel (net.ipv6.conf.all.disable_ipv6 = 0).

  2. Configure the Docker daemon (/etc/docker/daemon.json):

    {
      "ipv6": true,
      "ip6tables": true,
      "default-address-pools": [
        { "base": "fd00:qa::/48", "size": 64 }
      ]
    }

    Restart the daemon. ip6tables: true is required for cross-env isolation and published-port filtering; the pipeline trusts the attestation for it rather than probing (the agent user has docker-socket access only, not root).

  3. Set the Node Property QA_AGENT_IPV6_READY=true (Manage Jenkins → Nodes → agent → Configure → Node Properties → Environment variables). Set it only after the daemon is configured. This attestation doubles as the operator’s acknowledgement of the exposure note below.

Security: host-side port exposure

All service ports publish as bare HOST:CONTAINER (no 127.0.0.1 bind). On a daemon with ipv6+ip6tables, Docker installs IPv6 DNAT rules, so every published port becomes reachable on the agent’s IPv6 addresses — bypassing an IPv4-only host firewall. This repo does not manage host firewalling. Only onboard internet-facing agents for IPv6 after extending their IPv6 firewall rules to match the intended exposure.