Skip to main content
Star zrok on GitHub Star
Version: 2.0 (Current)

Run the zrok agent in Docker

The zrok2 agent runs in Docker using the docker.io/openziti/zrok2 container image. This guide covers two approaches:

  1. Bind-mount approach — mount your existing ~/.zrok2 directory from the Docker host. Quick to set up; requires enabling the zrok environment on the host first.
  2. Self-contained approach — use Docker named volumes and init containers to enable the environment automatically. No host-side setup required beyond an account token.

Both approaches run the agent as a long-lived container. Once the agent is running, interact with it using docker compose exec to create names, start shares, and manage the lifecycle.

Prerequisites

  • Docker with Compose v2
  • A zrok account token

Bind-mount approach

This approach mirrors the Linux agent service—you enable the zrok environment on the Docker host and mount it into the container.

  1. If you haven't already, install zrok2 and enable your account:

    zrok2 enable <your_account_token>

    Verify with:

    zrok2 status
  2. Save the following as compose.yml:

    compose.yml
    services:
    zrok-agent:
    image: docker.io/openziti/zrok2
    restart: unless-stopped
    user: "${UID:-1000}"
    environment:
    HOME: /mnt
    entrypoint:
    - bash
    - -c
    - rm -f /mnt/.zrok2/agent.socket && exec "$@"
    command:
    - --
    - zrok2
    - agent
    - start
    - --console-address
    - 0.0.0.0
    - --console-start-port
    - "8888"
    - --console-end-port
    - "8888"
    expose:
    - 8888 # agent web console
    # ports:
    # - 8888:8888 # uncomment to publish the agent console on the host
    volumes:
    - ${HOME}/.zrok2:/mnt/.zrok2
    Set the container user

    The user: "${UID:-1000}" directive sets the container's effective UID to match your Docker host user. This is required so the container can read and write to the bind-mounted ~/.zrok2 directory. If your UID is not 1000, set UID in your shell or .env file.

  3. Start the agent:

    docker compose up -d

    Verify it's running:

    docker compose logs zrok-agent
  4. Use docker compose exec to run zrok2 commands inside the running container:

    docker compose exec zrok-agent zrok2 agent status
    docker compose exec zrok-agent zrok2 create name myapp
    docker compose exec zrok-agent zrok2 share public http://host.docker.internal:8080 -n public:myapp
    host.docker.internal

    To share a service running on the Docker host, use host.docker.internal as the hostname. On Linux, you may also need --network=host on the container or add extra_hosts: ["host.docker.internal:host-gateway"] to the compose service.

Troubleshooting: ~/.zrok2 owned by root

If ~/.zrok2 doesn't exist on the Docker host when you first run docker compose up, Docker will create it automatically—but it will be owned by root. The agent container will fail with permission errors.

To fix this:

  1. Stop the container:

    docker compose down
  2. Fix ownership:

    sudo chown -R "$(id -u):$(id -g)" ~/.zrok2
  3. Enable the environment:

    zrok2 enable <your_account_token>
  4. Restart:

    docker compose up -d

To avoid this entirely, always run zrok2 enable on the host before the first docker compose up.

Self-contained approach

This approach uses Docker named volumes and init containers to manage the zrok environment entirely within Docker. No host-side zrok2 installation or ~/.zrok2 directory is needed — only an account token.

The zrok2-enable script included in the container image enables the environment if ZROK2_ENABLE_TOKEN is set, and does nothing if the environment is already enabled.

  1. Save the following as compose.yml:

    compose.yml
    services:
    # Set ownership on the named volume so the zrok2 user (UID 2171) can use it.
    zrok-init:
    image: busybox
    command: chown -Rc 2171:2171 /mnt/
    user: root
    volumes:
    - zrok_env:/mnt

    # Enable the zrok environment if not already enabled.
    zrok-enable:
    image: docker.io/openziti/zrok2
    depends_on:
    zrok-init:
    condition: service_completed_successfully
    entrypoint: zrok2-enable
    environment:
    HOME: /mnt
    ZROK2_ENABLE_TOKEN: ${ZROK2_ENABLE_TOKEN}
    ZROK2_API_ENDPOINT: ${ZROK2_API_ENDPOINT:-}
    volumes:
    - zrok_env:/mnt

    # Run the zrok agent.
    zrok-agent:
    image: docker.io/openziti/zrok2
    restart: unless-stopped
    depends_on:
    zrok-enable:
    condition: service_completed_successfully
    environment:
    HOME: /mnt
    entrypoint:
    - bash
    - -c
    - rm -f /mnt/.zrok2/agent.socket && exec "$@"
    command:
    - --
    - zrok2
    - agent
    - start
    - --console-address
    - 0.0.0.0
    - --console-start-port
    - "${ZROK2_AGENT_CONSOLE_PORT:-8888}"
    - --console-end-port
    - "${ZROK2_AGENT_CONSOLE_PORT:-8888}"
    expose:
    - "${ZROK2_AGENT_CONSOLE_PORT:-8888}"
    # ports:
    # - "${ZROK2_AGENT_CONSOLE_PORT:-8888}:${ZROK2_AGENT_CONSOLE_PORT:-8888}"
    volumes:
    - zrok_env:/mnt

    volumes:
    zrok_env:
  2. Create a .env file next to compose.yml:

    ZROK2_ENABLE_TOKEN=<your_account_token>
    # ZROK2_API_ENDPOINT=https://zrok.example.com # uncomment for self-hosted
    # ZROK2_AGENT_CONSOLE_PORT=8888 # agent web console port
  3. Start the agent:

    docker compose up -d

    The init containers run first (set volume permissions, then enable the environment). The agent starts automatically once the environment is ready.

  4. Interact with the agent using docker compose exec:

    docker compose exec zrok-agent zrok2 agent status
    docker compose exec zrok-agent zrok2 create name myapp
    docker compose exec zrok-agent zrok2 share public http://some-service:8080 -n public:myapp

Clean up

To destroy the environment and start fresh, remove the named volume:

docker compose down --volumes

Then delete the zrok environment from the web console or with zrok2 admin.

See also