Docker

The cleanest way to give one application a fixed identity without touching the host's routing at all.

Why a sidecar

A container can share another container's network namespace. So you run the tunnel in its own container and point your application at it: your app needs no WireGuard, no privileges and no source binding, and the host's own traffic is unaffected. Use a full tunnel config — inside the namespace, taking the default route is exactly what you want.

docker compose

docker-compose.yml

services:
  tunnel:
    image: lscr.io/linuxserver/wireguard:latest
    cap_add: [NET_ADMIN]
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
    volumes:
      - ./anchoredip-42.conf:/config/wg_confs/aip0.conf:ro
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wg", "show", "aip0", "latest-handshakes"]
      interval: 30s

  app:
    image: your/app:latest
    # Everything this container sends leaves as your leased address.
    network_mode: "service:tunnel"
    depends_on:
      tunnel:
        condition: service_healthy
    restart: unless-stopped
network_mode: service:tunnel means the app container has no network of its own. Publish ports on the tunnel service, not on the app — a ports: entry on the app is rejected, and this is the usual first error.

Plain docker run

docker network create aip-net

docker run -d --name aip-tunnel \
  --cap-add NET_ADMIN \
  --sysctl net.ipv4.conf.all.src_valid_mark=1 \
  -v "$PWD/anchoredip-42.conf:/config/wg_confs/aip0.conf:ro" \
  lscr.io/linuxserver/wireguard:latest

docker run -d --name app \
  --network "container:aip-tunnel" \
  your/app:latest

Verify

docker exec app curl -s https://api.ipify.org

Run it against the app container, not the tunnel one — the question is what your application looks like from outside, and that is the only answer that matters.

If the tunnel restarts

With network_mode: service:tunnel, the app's network dies with the tunnel container and does not come back on its own. Give both restart: unless-stopped and let the healthcheck above gate startup; on a tunnel restart, restart the app too.

One container per machine slot

Each tunnel config is one peer and counts against your plan's machine allowance. Do not copy one config into several containers — two peers presenting the same key fight over the same session and both break. Create a tunnel per container, or put several containers behind one sidecar, which is usually what you want.

Only one peer on the lease receives inbound traffic on the public address. For an outbound identity — the common case — every container behind every sidecar shares it. See use cases.