Skip to main content
Pylon uses a single machine-level config file at ~/.pylon/config.yaml to define how your daemon runs: which port it listens on, what Docker limits apply to agent containers, and which notification channel receives alerts by default. Running pylon setup creates this file for you and walks you through the core options. Secrets such as bot tokens and API keys belong in ~/.pylon/.env (one KEY=VALUE pair per line); Pylon loads that file automatically on pylon start and never reads it into version control.

Secrets files

Pylon reads secrets from two .env files and substitutes them into config fields that use ${VAR_NAME} syntax. Config files stay free of raw secrets, and you can commit them to a repo without leaking credentials.

File locations

File format

One KEY=VALUE pair per line. Lines starting with # are comments. Do not wrap values in quotes and do not use a shell export prefix.
Set restrictive permissions on every .env file: chmod 600 ~/.pylon/.env. pylon setup writes new files with mode 0600 automatically, but files you create by hand start with your umask default.

Referencing secrets in YAML

Use ${VAR_NAME} anywhere a string value is accepted:
Fields that commonly hold secrets: trigger.secret, channel.telegram.bot_token, channel.slack.bot_token, channel.slack.app_token, agent.api_key, and any value under agent.env.

Resolution order

When Pylon expands ${VAR_NAME} for a given pylon it checks sources in this order:
  1. The pylon’s own ~/.pylon/pylons/<name>/.env
  2. The process environment (which includes anything the global ~/.pylon/.env loaded at startup, plus whatever was already exported in your shell)
Per-pylon values win. Shell-exported values win over the global .env file — the global loader only sets a variable if it is not already defined.
Per-pylon .env files let each pylon use clean, generic names like TELEGRAM_BOT_TOKEN without worrying about collisions. When the same pylon needs two different tokens (for example, one Telegram bot for alerts and another for debug messages), suffix the names: TELEGRAM_BOT_TOKEN_ALERTS, TELEGRAM_BOT_TOKEN_DEBUG.
If a referenced variable is unset, Pylon rejects the config at startup with a message like telegram.bot_token references ${TELEGRAM_BOT_TOKEN} but it is not set. Run pylon doctor to surface these errors before you rely on the pylon at runtime.

Annotated config.yaml

Field reference

version

version
integer
required
Config schema version. Set this to 1. Pylon uses this to detect breaking changes in future releases.

server

server.port
integer
default:"8080"
HTTP port the Pylon daemon listens on for incoming webhook requests.
server.host
string
default:"0.0.0.0"
Network interface to bind. 0.0.0.0 accepts connections on all interfaces. Use 127.0.0.1 to restrict to localhost.
server.public_url
string
Base URL Pylon uses when printing webhook registration URLs (for example, https://myserver.example.com). Individual pylons can override this with their own trigger.public_url. If unset, Pylon falls back to http://<host>:<port>.

defaults.channel

defaults.channel.type
string
required
Default notification backend for all pylons. Accepted values: telegram, slack, webhook, stdout. Individual pylons can override this.
defaults.channel.telegram.bot_token
string
Telegram bot token from @BotFather. Use ${TELEGRAM_BOT_TOKEN} to pull from .env. Required when type is telegram.
defaults.channel.telegram.chat_id
integer
Telegram chat or group ID. Use a negative number for groups and supergroups. Required when type is telegram.
defaults.channel.telegram.allowed_users
integer[]
List of Telegram user IDs that may click the Approve or Ignore buttons on job notifications. If empty, approval buttons are not shown.
defaults.channel.slack.bot_token
string
Slack bot token (starts with xoxb-). Required when type is slack.
defaults.channel.slack.app_token
string
Slack app-level token (starts with xapp-). Used for Socket Mode. Required when type is slack.
defaults.channel.slack.channel_id
string
Slack channel ID (starts with C). Required when type is slack.
defaults.channel.slack.allowed_users
string[]
List of Slack user IDs that may approve or ignore jobs. If empty, approval actions are not shown.

defaults.agent

defaults.agent.type
string
default:"claude"
Default agent runtime. Accepted values: claude, opencode.
defaults.agent.claude.image
string
default:"ghcr.io/pylonto/agent-claude"
Docker image for Claude Code agent containers.
defaults.agent.claude.auth
string
default:"oauth"
Authentication method for Claude. oauth mounts your local OAuth session; api_key reads ANTHROPIC_API_KEY from the environment.
defaults.agent.claude.oauth_path
string
Path to your local ~/.claude/ directory. Pylon mounts it read-only into each agent container. Required when auth is oauth.
defaults.agent.opencode.image
string
default:"ghcr.io/pylonto/agent-opencode"
Docker image for OpenCode agent containers.
defaults.agent.opencode.auth
string
default:"none"
Authentication method for OpenCode. none uses OpenCode Zen (no API key required); api-key reads the relevant provider environment variable.
defaults.agent.opencode.provider
string
LLM provider when auth is api-key. Accepted values: anthropic, openai, google. Pylon automatically maps each provider to its standard API key environment variable (ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY).

docker

docker.max_concurrent
integer
default:"3"
Maximum number of agent containers that can run simultaneously. Jobs that arrive when all slots are occupied are currently rejected.
docker.default_timeout
string
default:"15m"
Default maximum run time for a single agent job. Accepts Go duration strings such as 10m, 1h30m. Individual pylons can override this with agent.timeout.