Skip to content

Configuration

VibePod merges configuration from four sources in order, with each layer overriding the previous:

  1. Built-in defaults
  2. Global config~/.config/vibepod/config.yaml
  3. Project config.vibepod/config.yaml in the current directory
  4. Environment variables — override specific keys at runtime

Run vp config path to print the exact paths in use, and vp config show to print the fully merged result.

Full reference

# Config format version (always 1)
version: 1

# Agent to run when no argument is given to `vp run`
# Alias `vibe` resolves to `devstral`.
default_agent: claude

# Pull the latest image before every run (default: true)
# Can be overridden per agent with agents.<agent>.auto_pull
auto_pull: true

# Remove untagged vibepod images after a pull (default: true)
# When a pull retags `latest`, the previous image stays behind untagged
# (`vibepod/claude   <none>`) and such images accumulate over time. With
# auto_clean every pull sweeps the whole `vibepod/` namespace, so leftovers
# from earlier pulls are cleared too. Tagged images, images of other
# namespaces, and images still used by a container are never removed — an
# image held by a container is picked up by a later sweep instead.
auto_clean: true

# Remove the container automatically when it stops (default: true)
auto_remove: true

# Primary Docker network created and used by VibePod
network: vibepod-network

# Log level: debug | info | warning | error
log_level: info

# Disable colour output
no_color: false

agents:
  claude:
    enabled: true
    image: vibepod/claude:latest
    auto_pull: null # Per-agent override: true/false, or null to use global auto_pull
    env: {} # Extra environment variables passed to the container
    volumes: [] # Reserved for future use
    ports: [] # Ports to publish on the host, `docker run -p` syntax
    init: [] # Optional shell commands run before agent startup
    overlay: true # Set false to ignore the project's .vibepod/overlay/ (see Project overlays)
    # acp_command: ACP adapter command for `vp run <agent> --acp` (editor
    # integration via the Agent Client Protocol). Defaults exist for claude,
    # gemini, qwen, codex, opencode, copilot, auggie, jcode and devstral;
    # override with a list, or a string parsed with shell quoting rules. Not
    # present by default — just add the key to override.
    # acp_command: ["npx", "-y", "@agentclientprotocol/claude-agent-acp"]

  gemini:
    enabled: true
    image: vibepod/gemini:latest
    env: {}
    volumes: []
    ports: []
    init: []

  opencode:
    enabled: true
    image: vibepod/opencode:latest
    env: {}
    volumes: []
    ports: []
    init: []

  devstral:
    enabled: true
    image: vibepod/devstral:latest
    env: {}
    volumes: []
    ports: []
    init: []

  auggie:
    enabled: true
    image: vibepod/auggie:latest
    env: {}
    volumes: []
    ports: []
    init: []

  copilot:
    enabled: true
    image: vibepod/copilot:latest
    env: {}
    volumes: []
    ports: []
    init: []

  codex:
    enabled: true
    image: vibepod/codex:latest
    env: {}
    volumes: []
    ports: []
    init: []

  pi:
    enabled: true
    image: vibepod/pi:latest
    env: {}
    volumes: []
    ports: []
    init: []

  agy:
    enabled: true
    image: vibepod/agy:latest
    env: {}
    volumes: []
    ports: []
    init: []

  tau:
    enabled: true
    image: vibepod/tau:latest
    env: {}
    volumes: []
    ports: []
    init: []

  jcode:
    enabled: true
    image: vibepod/jcode:latest
    env: {}
    volumes: []
    init: []

  freebuff:
    enabled: true
    image: vibepod/freebuff:latest
    env: {}
    volumes: []
    ports: []
    init: []

  qwen:
    enabled: true
    image: vibepod/qwen:latest
    env: {}
    volumes: []
    ports: []
    init: []

  dsh:
    enabled: true
    image: vibepod/dsh:latest
    env: {}
    volumes: []
    ports:
      - "127.0.0.1:3080:3081" # Web UI; container side must stay 3081
    init: []

  hermes:
    enabled: true
    image: vibepod/hermes:latest
    env: {}
    volumes: []
    ports: []
    init: []

# Connect agents to a local or remote LLM server (Ollama, vLLM, etc.)
llm:
  enabled: false
  base_url: "" # Server endpoint URL
  api_key: "" # Auth token (set to "ollama" for Ollama)
  model: "" # Model name passed to the agent

logging:
  enabled: true
  image: vibepod/datasette:latest
  db_path: ~/.config/vibepod/logs.db
  ui_port: 8001 # Port for the Datasette UI

proxy:
  enabled: true
  image: vibepod/proxy:latest
  db_path: ~/.config/vibepod/proxy/proxy.db
  ca_dir: ~/.config/vibepod/proxy/mitmproxy
  ca_path: ~/.config/vibepod/proxy/mitmproxy/mitmproxy-ca-cert.pem
  filter:
    mode: open # open | allow | deny
    allow: []
    deny: []

Environment variables

These variables override the corresponding config keys without editing any file:

Variable Config key Example
VP_DEFAULT_AGENT default_agent VP_DEFAULT_AGENT=vibe
VP_AUTO_PULL auto_pull VP_AUTO_PULL=true
VP_AUTO_CLEAN auto_clean VP_AUTO_CLEAN=false
VP_LOG_LEVEL log_level VP_LOG_LEVEL=debug
VP_NO_COLOR no_color VP_NO_COLOR=true
VP_DATASETTE_PORT logging.ui_port VP_DATASETTE_PORT=9001
VP_PROXY_ENABLED proxy.enabled VP_PROXY_ENABLED=false
VP_PROXY_FILTER_MODE proxy.filter.mode VP_PROXY_FILTER_MODE=allow
VP_LLM_ENABLED llm.enabled VP_LLM_ENABLED=true
VP_LLM_BASE_URL llm.base_url VP_LLM_BASE_URL=http://localhost:11434
VP_LLM_API_KEY llm.api_key VP_LLM_API_KEY=ollama
VP_LLM_MODEL llm.model VP_LLM_MODEL=qwen3:14b
VP_CONFIG_DIR (config root) VP_CONFIG_DIR=/custom/path
VP_PROFILE profile VP_PROFILE=work

The profile key selects the active credential profile; the --profile flag on vp run, vp task create, and vp doctor claude takes precedence over both the variable and the config key.

Image overrides

Each agent image can be overridden individually:

Variable Agent
VP_IMAGE_CLAUDE claude
VP_IMAGE_GEMINI gemini
VP_IMAGE_OPENCODE opencode
VP_IMAGE_DEVSTRAL devstral
VP_IMAGE_AUGGIE auggie
VP_IMAGE_COPILOT copilot
VP_IMAGE_CODEX codex
VP_IMAGE_PI pi
VP_IMAGE_AGY agy
VP_IMAGE_TAU tau
VP_IMAGE_JCODE jcode
VP_IMAGE_FREEBUFF freebuff
VP_IMAGE_QWEN qwen
VP_IMAGE_DSH dsh
VP_IMAGE_HERMES hermes
VP_DATASETTE_IMAGE datasette (logs UI)
VP_PROXY_IMAGE proxy
VP_SKILLS_ENGINE_IMAGE skills-engine (used by vp skills)

Set VP_IMAGE_NAMESPACE to change the prefix for all default images at once:

VP_IMAGE_NAMESPACE=myorg vp run claude
# pulls myorg/claude:latest

For end-to-end examples (extending a base image and assigning a brand-new image to an agent), see Agents > Image customization workflows.

Project-level config

Use vp config init in your repository to create .vibepod/config.yaml automatically when it does not already exist:

vp config init

If .vibepod/config.yaml already exists, this command exits without modifying it; run vp config init --force to replace the file.

This writes a minimal starter file:

version: 1

To copy a full agent block into the project config, pass the agent name:

vp config init claude

This adds agents.claude to .vibepod/config.yaml (creating the file if needed). If agents.claude is already present, the command exits without modifying the file.

Then add only the keys you want to override. Project config is merged on top of global config and defaults.

# .vibepod/config.yaml
default_agent: opencode
agents:
  opencode:
    env:
      OPENAI_BASE_URL: https://my-internal-proxy/v1
    init:
      - apt-get update
      - apt-get install -y ripgrep

agents.<agent>.init runs inside the container before the agent process starts. Commands run with /bin/sh -lc and set -e (startup stops on the first failed command).

Commit this file to share project defaults with your team.

Publishing ports

By default no user-defined ports are published, so anything the agent starts inside the container — a dev server, web UI, or debugger — is unreachable from the host. agents.<agent>.ports publishes container ports on the host using the same syntax as docker run -p:

# .vibepod/config.yaml
agents:
  claude:
    ports:
      - "8000:8000" # host:container
      - "127.0.0.1:9229:9229" # bind to a specific host interface
      - "6000:6000/udp" # UDP
      - "3000" # container port on a random host port

Always quote port entries

YAML 1.1 parses unquoted host:container pairs as base-60 numbers: - 22:22 loads as the integer 1342 and would publish the wrong port. Out-of-range results (e.g. 3000:30180030) are rejected at startup, but in-range ones are not detectable — quote every entry.

Like other agent keys, a project-level ports list replaces the global one for that agent. The list applies to both vp run and vp task containers; note that two containers cannot publish the same host port at the same time, so a fixed host port limits you to one such container per agent.

For a one-off override, vp run (and the agent alias commands) accept a repeatable -p/--publish flag with the same syntax. When given, it replaces the resolved config list for that run — the last level of the defaults → global → project → CLI chain:

vp run claude -p 127.0.0.1:3090:3081 -p 6000:6000/udp

The built-in proxy

VibePod starts a vibepod-proxy container alongside every agent. It acts as an HTTP(S) MITM proxy and logs all outbound requests to a SQLite database viewable in the Datasette UI (vp logs start).

The proxy is reachable inside the Docker network as http://vibepod-proxy:8080. It is not published on a host port.

To disable the proxy globally:

proxy:
  enabled: false

Or at runtime:

VP_PROXY_ENABLED=false vp run claude

Podman users

The proxy relies on container-name DNS resolution over the vibepod-network network. If you use Podman with the CNI backend, install the dnsname plugin (e.g. podman-plugins on Fedora, golang-github-containernetworking-plugin-dnsname on Debian/Ubuntu) and recreate the network. See Quickstart — Using Podman for full instructions.

Allow/deny filtering

Filtering is opt-in; the default mode open passes and logs everything (today's behavior). In allow mode only listed hosts pass; in deny mode everything passes except listed hosts. Patterns: example.com matches that host exactly, *.example.com matches subdomains (not the apex).

vp proxy filter status
vp proxy filter mode allow
vp proxy filter allow add api.anthropic.com
vp proxy filter allow add "*.github.com"
vp proxy filter deny add example.com
vp proxy filter mode open        # back to no filtering; lists are kept

One shared proxy evaluates a separate policy for every VibePod agent container. Each launch receives an opaque policy identity and is also bound to its source container after startup. This prevents a later launch from replacing the rules of an already-running agent. Unidentified clients continue to use the global filter.

Profile changes apply immediately to every running container using that profile—no proxy restart is needed. Project filter settings and VP_PROXY_FILTER_MODE are captured when each container starts, so changing either affects new launches only. The effective profile and live mode are shown by vp list in the PROFILE and PROXY MODE columns and in its JSON rows as profile and proxy_mode.

Blocked requests return 403 (HTTPS tunnels are refused at CONNECT) and are logged with blocked = 1 in the proxy database. Invalid policy configuration is rejected instead of silently falling back to open; an identified launch whose policy files are missing or malformed fails closed.

The vp proxy filter commands act on the active profile. For the default profile they write the global config; for a named profile they write profiles/<name>/filter.yaml (seeded from the global settings on first write), so switching profiles also switches the filter mode and lists. Pass --profile <name> to manage another profile's filter without switching.

A project-level .vibepod/config.yaml filter section or VP_PROXY_FILTER_MODE takes precedence over the global values (for a profile with its own filter.yaml, only VP_PROXY_FILTER_MODE still overrides the mode). Project and environment overrides are launch-specific and never replace the shared global fallback.

Custom proxy images must implement per-source policies and expose this exact OCI image label:

LABEL io.vibepod.proxy.policy-schema="2"

VibePod checks the configured image—and an already-running proxy—before launching a proxied agent. An image with a missing or different schema label is rejected with an upgrade error.

Filtering is not a network sandbox

The filter controls requests that use the injected proxy. A container can override its proxy environment or attempt a direct connection unless a separate network-control layer prevents that. VibePod warns when explicit HTTP_PROXY or HTTPS_PROXY values bypass its identified proxy URL.