Skip to content

pom.yml reference ​

Every key Pomelo reads from pom.yml, the file at the project root that says how to build and run the project. It holds only that: the app's own behavior lives in Settings. Values can use templates. Pomelo ignores a key not listed here.

Top level ​

The keys at the top of pom.yml.

KeyTypeDefaultWhat it does
sessionstringpomeloThe project's name. It prefixes database names, holders, hostnames and the shared Docker compose project.
default_branchstringmainThe branch every repo's main workspace follows; a repo's default_branch overrides it.
reposmap-The project's repositories; see Repos.
shared_servicesmap-Services one instance of serves every workspace; see Shared services.
presetsmap-Reusable repo fragments; see Presets.
environmentsmap of maps-Profiles that point services at deployed URLs: <profile>: { <repo>.<service>: <url> }. Under that profile {{<repo>.<service>.url}} (and .host, .port, .ws) resolves to the URL and the dev proxy forwards there; other services stay local. The URL is used as written, without templates.
presetstring or list-Presets whose services run once per workspace, outside any repo.
seedlist of commands-Runs once in the workspace folder when a workspace is created, before each repo's seed.
prepare_mainlistreset, migrate, seed, snapshotThe phases Prepare Main runs, in order: reset, migrate, seed and snapshot, which saves main's databases as the main__baseline snapshot new workspaces copy from without disconnecting main. Other names are skipped; a list with none of them runs only reset.
syncmap-Keep Main Fresh; see Sync.
agentsmap-A policy for the workspace's coding agents; see Agents.
workspacesmap-Ignored. Workspace groups; nothing reads them now.
combinationsmap-Ignored. Repo combinations; config_normalize deletes them.
code_agentsmap-Ignored. Agent switches; the app's Settings hold these now.
uimap-Ignored. UI preferences; the app's Settings hold these now.
pluginsmap-Ignored. Plugin settings; config_normalize deletes them.

Examples:

yaml
session: myproject

default_branch: main

environments:
  staging:
    api.server: https://api.staging.example.com

preset: [gateway]

seed: [./scripts/seed-all.sh]

prepare_main: [migrate, seed, snapshot]

Repos ​

Each entry of repos: is one repository, keyed by its clone folder name.

KeyTypeDefaultWhat it does
repos.<repo>.aliasstringthe repo's keyThe short name hostnames and templates use for this repo ({{<alias>.<service>.url}}; the key works too). Rename Alias in Settings > Project rewrites the references.
repos.<repo>.default_branchstringthe top-level default_branchThis repo's main branch, when it differs from the project's.
repos.<repo>.presetstring or list-Presets to apply; they only fill what the repo left unset, in order.
repos.<repo>.databasesmap-Databases each workspace gets, created automatically: <name>: <name template>, named <session>_<resolved template>. Reach one with {{db.<name>}}. Only the branch tokens work in the template.
repos.<repo>.shared_serviceslist-The shared services this repo uses; see A repo's shared services. Starting the repo's services starts these. A declared shared service no repo lists is flagged by config_doctor (shared.unwired).
repos.<repo>.seed_from_mainboolfalseNew workspaces copy this repo's databases from the main workspace instead of starting empty, and skip its seed.
repos.<repo>.profileslist-The environments profiles this repo's services may switch between; local is always offered. Each must be defined in environments.
repos.<repo>.proxy_portport-Ignored. Only a fallback port for a template that names no service; service ports are leased.
repos.<repo>.shell_envstring-KEY=value ... put before every service command of the repo.
repos.<repo>.envmap-The repo's environment, with templates resolved; services get it injected, and it is written to .env.local where the repo keeps env files (a root .env or .env.development, apps/<name>/.env, a service's dir). If any value is a map, the keys are file names instead: each file gets its own keys on top of *, the base for every file. Edit it here, never in the generated file.
repos.<repo>.servicesmap-The repo's long-running processes; see Services.
repos.<repo>.lifecyclemap-How the repo is built and run; see Repo lifecycle.
repos.<repo>.pre_startcommand-Same as lifecycle.pre_start.
repos.<repo>.commandsmap-Same as lifecycle.commands.
repos.<repo>.setuplist of commands-Same as lifecycle.setup.
repos.<repo>.migratelist of commands-Same as lifecycle.migrate.
repos.<repo>.seedlist of commands-Same as lifecycle.seed.
repos.<repo>.pre_deletelist of commands-Same as lifecycle.pre_delete.
repos.<repo>.copylist of globs-Same as lifecycle.copy.
repos.<repo>.taskslist-Same as lifecycle.tasks.
repos.<repo>.shortcutslist-Older name of tasks.
repos.<repo>.pluginsmap-Ignored. Plugin settings; config_normalize deletes them.

Examples, each written under repos.<repo>:

yaml
alias: api

preset: [rails]

databases:
  main: "{{branch.safe}}"
  test: "{{branch.safe}}_test"

shared_services: [postgres, redis]

seed_from_main: true

profiles: [staging]

shell_env: RAILS_LOG_TO_STDOUT=1

env:
  DATABASE_URL: postgresql://{{shared.postgres.url}}/{{db.main}}

env:
  "*":
    REDIS_URL: redis://{{shared.redis.host}}:{{shared.redis.port}}/{{shared.redis.slot}}
  .env.development.local:
    DATABASE_URL: postgresql://{{shared.postgres.url}}/{{db.main}}
  .env.test.local:
    DATABASE_URL: postgresql://{{shared.postgres.url}}/{{db.test}}

Repo lifecycle ​

A repo's lifecycle: block groups how it is built and run. Every key here also works directly on the repo; when both are set, the lifecycle: value wins.

KeyTypeDefaultWhat it does
repos.<repo>.lifecycle.pre_startcommand-Runs before every service of the repo starts, in the same shell (for example nvm use).
repos.<repo>.lifecycle.commandsmap-Named commands. Each becomes a task; install, generate and migrate, in that order, are the setup a new workspace runs unless setup is set, and migrate is what Prepare Main and refreshes run unless migrate is set.
repos.<repo>.lifecycle.setuplist of commandscommands install, generate, migrateSteps run right after the worktree is created, in the worktree with the repo's env, joined with &&.
repos.<repo>.lifecycle.migratelist of commandscommands.migrateThe repo's migration steps, run by Prepare Main and Keep Main Fresh.
repos.<repo>.lifecycle.seedlist of commands-Runs after setup when a workspace is created; skipped when seed_from_main copies the databases.
repos.<repo>.lifecycle.pre_deletelist of commands-Runs in the repo before its worktree is deleted; a failure only warns.
repos.<repo>.lifecycle.copylist of globs-Files copied from the main workspace's checkout into each new worktree; * works in the last path part, and files the worktree already has are kept.
repos.<repo>.lifecycle.taskslist-Quick commands; see Tasks.
repos.<repo>.lifecycle.shortcutslist-Older name of tasks.

Examples, each written under repos.<repo>.lifecycle:

yaml
pre_start: nvm use

commands:
  install: bundle install
  migrate: bin/rails db:migrate
  test: bin/rspec

setup: [npm ci, npm run build]

seed: [bin/rails db:seed]

copy: [config/master.key]

Services ​

Each entry of a repo's (or preset's) services: is one long-running process. name: <cmd> is short for name: { cmd: <cmd> }.

KeyTypeDefaultWhat it does
repos.<repo>.services.<service>.cmdcommand-The command that runs the service, in the login shell with the resolved env. It gets $PORT and $BIND_IP.
repos.<repo>.services.<service>.typebackend, frontend or worker-A backend or frontend gets a $PORT unless port: false; a worker gets none unless port: true.
repos.<repo>.services.<service>.dirpaththe repo folderThe folder, inside the repo, the service runs in (a monorepo app).
repos.<repo>.services.<service>.portbooltrue for backend and frontendWhether the service gets a leased $PORT.
repos.<repo>.services.<service>.depends_onlist-Services of the same repo that start first.
repos.<repo>.services.<service>.envmap-Environment for this service only, on top of the repo's.
repos.<repo>.services.<service>.pre_startcommandthe repo's pre_startReplaces the repo's pre_start for this service.
repos.<repo>.services.<service>.shell_envstringthe repo's shell_envReplaces the repo's shell_env for this service.
repos.<repo>.services.<service>.proxy_portport-Ignored. Nothing reads it; the dev proxy forwards to the leased port.
repos.<repo>.services.<service>.profileslistthe repo's profilesReplaces the repo's profiles for this service.
repos.<repo>.services.<service>.modesmap-Named alternative commands, switched in the app without editing the config.
repos.<repo>.services.<service>.modestring-The mode used when none is picked in the app; cmd runs when there is none.
repos.<repo>.services.<service>.taskslist-Quick commands for this service; see Tasks.
repos.<repo>.services.<service>.shortcutslist-Older name of tasks.
repos.<repo>.services.<service>.healthcheckmap-When the service counts as ready; see Service healthcheck.
repos.<repo>.services.<service>.queuemap-The job queue this worker drains; see Service queue.

Examples, each written under repos.<repo>.services.<service>:

yaml
cmd: bin/rails s -p $PORT -b $BIND_IP

type: backend

dir: apps/web

port: false

depends_on: [server]

modes:
  dev: npm run dev -- --port $PORT
  prod: npm run start -- -p $PORT

mode: dev

healthcheck: { http: /health }

queue: { kind: sidekiq }

Service healthcheck ​

When a service counts as ready, for pom start --wait and pom status: give http or cmd. Without a healthcheck a service with a port is ready once the port listens.

KeyTypeDefaultWhat it does
repos.<repo>.services.<service>.healthcheck.httppath-A path on the service's own port; ready once a GET answers 2xx or 3xx.
repos.<repo>.services.<service>.healthcheck.cmdcommand-A shell command run in the service's folder with its env; ready once it exits 0.
repos.<repo>.services.<service>.healthcheck.intervalduration1sTime between checks.
repos.<repo>.services.<service>.healthcheck.timeoutduration3sHow long one check may take before it counts as failed.

Examples, each written under repos.<repo>.services.<service>.healthcheck:

yaml
http: /health

cmd: bin/rails runner 'ActiveRecord::Base.connection'

interval: 500ms

timeout: 5s

Service queue ​

The background-job queue a worker service drains, in the workspace's Redis slot. pom queue wait-idle <service> waits until it is empty, e.g. before a test checks what a job did.

KeyTypeDefaultWhat it does
repos.<repo>.services.<service>.queue.kindstring-sidekiq or bullmq.
repos.<repo>.services.<service>.queue.prefixstringbullBullMQ's key prefix.
repos.<repo>.services.<service>.queue.queueslistevery queue foundThe queues to watch.
repos.<repo>.services.<service>.queue.redisstringthe repo's first RedisThe shared Redis service the queue lives in.

Examples, each written under repos.<repo>.services.<service>.queue:

yaml
kind: bullmq

prefix: bull

queues: [default, mailers]

redis: redis

Tasks ​

A tasks: list (older name shortcuts:) on a repo, its lifecycle, a service or a preset adds quick commands to the app and the agents.

KeyTypeDefaultWhat it does
tasks[].keystring-A short name for the task.
tasks[].descstring-What the task does, shown next to it.
tasks[].cmdcommand-The command the task runs, in the repo.

Examples, each written under tasks[]:

yaml
key: migrate

desc: Run migrations

cmd: bin/rails db:migrate

A repo's shared services ​

A repo's shared_services: lists the shared services it uses: a name, or name: { db_name: <template> }.

KeyTypeDefaultWhat it does
repos.<repo>.shared_services[].<name>.db_nametemplate-The database this repo uses on that shared service, as a name template taking the branch tokens.

Examples, each written under repos.<repo>.shared_services[].<name>:

yaml
db_name: "{{branch.safe}}_reports"

Shared services ​

Each entry of shared_services: runs once for every workspace: a Docker image or a cmd, never both. postgres, redis, minio, opensearch and zincsearch (by name or type:) get a working image, ports, credentials and healthcheck filled in.

KeyTypeDefaultWhat it does
shared_services.<name>.typestringthe service's nameThe well-known service to fill defaults from, when the name differs: postgres, redis, minio, opensearch or zincsearch.
shared_services.<name>.imagestringfilled for well-known servicesThe Docker image to run. Either this or cmd.
shared_services.<name>.cmdcommand-A command run once for every workspace instead of a container. It gets $PORT and $BIND_IP; {{shared.<name>.url}} is http://127.0.0.1:<port>. Either this or image.
shared_services.<name>.reporepo keythe project folderFor a cmd: the repo whose main-workspace checkout it runs in. Must be in repos.
shared_services.<name>.portportleasedFor a cmd: the port it is told in $PORT. No other shared service may want it.
shared_services.<name>.portslistfilled for well-known servicesFor an image: the container ports to publish ("5432" or "host:container"). The first number is the preferred host port; Pomelo keeps it when free, else takes the next free one within 100, else a random one, and the choice sticks. Templates read it as {{shared.<name>.port}}.
shared_services.<name>.environmentmapfilled for well-known servicesEnvironment for the container or command. In a cmd service, $PORT and ${PORT} in a value become its port.
shared_services.<name>.volumeslistfilled for well-known servicesFor an image: Docker volumes.
shared_services.<name>.commandstring-For an image: the container's command.
shared_services.<name>.healthcheckmapfilled for well-known servicesWhen the service counts as up; see Shared service healthcheck.
shared_services.<name>.db_userstringpostgres for a PostgresFor an image: the login {{shared.<name>.url}} and .user carry.
shared_services.<name>.db_passwordstringpostgres for a PostgresFor an image: the password {{shared.<name>.url}} and .pass carry.
shared_services.<name>.capacitynumber-For an image: how many workspaces share one instance. Each gets a slot, {{shared.<name>.slot}} (a Redis database number, for example); when an instance is full another starts at base port + instance.
shared_services.<name>.slot_resetstring-For an image with capacity: a command run inside the instance's container to empty one slot, with {{slot}} replaced by its number. It runs when a workspace gives its slot back (deleted, or its folder is gone) and before a slot is handed to a new workspace, so no workspace sees another's data; a slot whose reset fails is not handed out. The redis preset empties the Redis database.
shared_services.<name>.hoststringlocalhostThe host the app's database and storage browsers connect to. Templates always use 127.0.0.1.

Examples, each written under shared_services.<name>:

yaml
type: postgres

image: postgres:16

cmd: node scripts/mock-as.js

repo: api

port: 4010

ports: ["5432"]

capacity: 64

slot_reset: redis-cli -n {{slot}} FLUSHDB | grep -qx OK

Shared service healthcheck ​

When the shared service counts as up.

KeyTypeDefaultWhat it does
shared_services.<name>.healthcheck.testcommand or list-A shell command, or a Docker-style [CMD, ...] list, that succeeds once the service is up. A cmd service waits up to 30 seconds for it before the services that use it start.
shared_services.<name>.healthcheck.intervalduration-For an image: time between checks.
shared_services.<name>.healthcheck.timeoutduration-For an image: how long one check may take.
shared_services.<name>.healthcheck.retriesnumber-For an image: failed checks before the container counts as unhealthy.

Examples, each written under shared_services.<name>.healthcheck:

yaml
test: curl -sf http://127.0.0.1:$PORT/health

interval: 5s

timeout: 3s

retries: 10

Presets ​

Each entry of presets: is a reusable repo fragment. A repo (or the workspace, with the top-level preset:) names presets; a preset only fills what the repo left unset. Inside a preset, write the lifecycle keys flat (setup:, commands:), not under lifecycle:.

KeyTypeDefaultWhat it does
presets.<preset>.presetstring or list-Presets this one builds on; they apply first.
presets.<preset>.servicesmap-Services the preset adds, with the fields of Services; one is added only when the repo has none by that name.
presets.<preset>.envmap-Environment keys the repo left unset, merged key by key. Keep it a flat map.
presets.<preset>.pre_startcommand-As on a repo.
presets.<preset>.commandsmap-As on a repo, merged key by key.
presets.<preset>.setuplist of commands-As on a repo.
presets.<preset>.migratelist of commands-As on a repo.
presets.<preset>.seedlist of commands-As on a repo.
presets.<preset>.pre_deletelist of commands-As on a repo.
presets.<preset>.copylist of globs-As on a repo.
presets.<preset>.seed_from_mainboolfalseAs on a repo.
presets.<preset>.taskslist-As on a repo.
presets.<preset>.shortcutslist-Older name of tasks.

Sync ​

Keep Main Fresh: pulling the main workspace on a schedule.

KeyTypeDefaultWhat it does
sync.auto_pushboolfalsePush committed work on a timer.
sync.interval_secnumber180Seconds between pushes when auto_push is on; at least 30.
sync.refresh_mainboolfalseKeep Main Fresh: pull and migrate the main workspace on a schedule. Once it is set in the app, the app's choice wins.
sync.refresh_interval_secnumber1800Seconds between Keep Main Fresh runs.

Examples, each written under sync:

yaml
refresh_main: true

refresh_interval_sec: 1800

Agents ​

What the workspace's coding agents may do: a policy command Pomelo asks before every tool call an agent makes.

KeyTypeDefaultWhat it does
agents.policycommand-Run before every tool call a coding agent in a workspace makes, in the workspace folder. It reads {tool_name, tool_input, session_id, role, workspace, origin, driven_by} as JSON on stdin and prints {"decision": "allow" | "deny" | "ask", "reason": "..."}. ask shows the agent's permission prompt; in a session an orchestrator drives it denies with pending approval <id> until pom agent approve records an approval for that call. A failure, a non-zero exit, a timeout or an unreadable config denies the call. Without a policy, the agent's own permission prompts apply.
agents.policy_timeout_secint5How long the policy command may take before the tool call is denied.

Examples, each written under agents:

yaml
agents:
  policy: ./scripts/agent-policy.sh

policy_timeout_sec: 10

code_agents (ignored) ​

Read so older files load, but nothing uses it: agent behavior is in the app's Settings.

KeyTypeDefaultWhat it does
code_agents.disabledbool-Ignored. Nothing reads it.
code_agents.onlylist-Ignored. Nothing reads it.
code_agents.notify_disabledbool-Ignored. Nothing reads it.

ui (ignored) ​

Read so older files load, but nothing uses it: the app's Settings hold this.

KeyTypeDefaultWhat it does
ui.editorstring-Ignored. Nothing reads it; Settings > Editor > External Editor replaced it.

Removed keys ​

These keys do nothing any more. config_normalize (or pom config normalize) deletes the ones it can.

KeyInstead
schema_versionVersion marker of an older format.
proxyRouting is automatic: /_pom_dev/<repo>/<service> and <service>.<repo>.<branch>.localhost.
webhookWebhooks are routed automatically at /<repo>/<service>.
global_servicesReplaced by shared_services.
shared_stable_portsShared service ports are leased automatically.
e2eRemoved with exposes: and {{var:}}.
jiraThe app's Settings hold the Jira connection.
archiveRemoved.
repos.<repo>.exposesPublished a {{var:}} variable; use a service ref such as {{<repo>.<service>.url}}.
repos.<repo>.env_switchProfiles switch services through environments.
repos.<repo>.lifecycle.createWorkspace create runs setup (or the install, generate and migrate commands).
repos.<repo>.lifecycle.refreshRefreshes run migrate.
repos.<repo>.services.<service>.exposesPublished a {{var:}} variable; use a service ref such as {{<repo>.<service>.url}}.