Make your own workbench template
By the third project you notice you are typing the same thing again. Your stack, your database, the agent, and the plugin your team keeps its conventions in. You know the command by heart, which is exactly the problem: it lives in your head, and everyone else on the team has their own version of it in theirs.
A template is that setup as a file. monoceros init can start from one instead
of from nothing, and because a template is an ordinary workbench yml, making
your own means copying a config that already works and changing two lines.
Start from one that ships
Section titled “Start from one that ships”Four templates ship with the CLI. Two are a stack to edit down, web-app and
go-service, and two carry a way of working and no stack,
discovery-atlassian and agent-github. Start with whichever is closest; the
init reference lists what each one
holds.
web-app is a Node front end, a
Java back end, a Postgres,
a Keycloak for the logins, and
Claude Code.
monoceros init acme --template=web-appInit asks for the credentials those components need, writes the config and
stops. Nothing is built yet. Press Tab after --template= to see which
templates you have.
Add what this project needs
Section titled “Add what this project needs”You are not stuck with what the template holds. The --with-* flags still work
and are applied on top of it, so you take the ready-made setup and put this
project’s own pieces next to it:
monoceros init acme --template=web-app --with-repos=https://gitlab.com/acme/api.gitInit then asks for the values it cannot know, one per question, with the
component’s own explanation underneath. It skips a value you already keep in
monoceros-config.env. Here, the Claude key and a GitLab token:
Your answers go straight into acme.env, and the list at the end says where
each value came from. Press Enter on one and the key stays empty in acme.env.
If you keep that key in your global monoceros-config.env, the question says
so, and Enter means that value is used. Without one, the Claude key stays empty,
which is what you want when you sign in with a subscription. Nothing else is asked: Postgres and Keycloak bring
working dev credentials, so there is nothing to invent there.
What you end up with is the template plus the repo, and the repo brought its own
support along: Monoceros saw a GitLab URL and added the
glab CLI, so pushing from inside the container works
with the token you just typed. The two files, with the comments and the options
left at their defaults dropped for brevity:
languages: - node:22 - java: version: latest installMaven: true installGradle: trueservices: - name: postgres image: postgres:18 port: 5432 env: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB} - name: keycloak image: quay.io/keycloak/keycloak:26.7 port: 8080 command: start-dev --import-realm --proxy-headers=xforwardedfeatures: - ref: ghcr.io/getmonoceros/monoceros-features/claude-code:1 options: permissionMode: auto apiKey: ${CLAUDE_CODE_API_KEY} - ref: ghcr.io/getmonoceros/monoceros-features/gitlab-cli:1 options: apiToken: ${GITLAB_API_TOKEN}repos: - url: https://gitlab.com/acme/api.gitGIT_USER_NAME=GIT_USER_EMAIL=GITLAB_API_TOKEN=glpat-3mK9wQzR7fL2xN8vCLAUDE_CODE_API_KEY=sk-ant-api03-7Rk2mQPOSTGRES_USER=monocerosPOSTGRES_PASSWORD=monocerosPOSTGRES_DB=monocerosKC_BOOTSTRAP_ADMIN_USERNAME=adminKC_BOOTSTRAP_ADMIN_PASSWORD=adminA component the template already carries stays as the template set it. One that clashes with it, the same feature with different options, is the same error it would be on the command line, and init writes nothing at all in that case.
Answer once, not per workbench
Section titled “Answer once, not per workbench”The Claude key is the same in every workbench you build, and so is your Atlassian token. Typing it again for each one is not the idea.
Put such a value in monoceros-config.env in your Monoceros home, and every
workbench on the machine can draw on it:
CLAUDE_CODE_API_KEY=sk-ant-api03-7Rk2mQInit still asks, because it cannot tell which of its questions you have already
answered globally. Press Enter and the key is written empty into acme.env,
which is exactly right: at apply the two files are merged, and an empty entry
falls through to the global one. An empty key does not blank out what you have
in the pool.
The other direction works too, and that is the reason the question is asked at
all. Type a value and it lands in acme.env, where it wins over the global one
for this workbench only. That is how one project gets the token of a different
customer without touching anything else.
Secrets and the .env file
has the whole model, including how an acme.env entry can point at a named
value in the pool rather than copying it.
Build the setup you keep rebuilding
Section titled “Build the setup you keep rebuilding”None of the shipped four is your setup, and they are not supposed to be. Yours has the services you actually run, the agent configured the way your team likes it, and the plugin marketplace your conventions live in. So build it once, as an ordinary workbench:
monoceros init proj-one --with-languages=go --with-services=postgres,mailpit --with-features=claude,githubThat is Go, a Postgres, a Mailpit to catch the mail the service sends, and Claude Code with the GitHub CLI.
Then open proj-one.yml and put in what no flag can express, the
plugin block on Claude Code being the usual
one:
- ref: ghcr.io/getmonoceros/monoceros-features/claude-code:1 options: permissionMode: auto plugins: - url: https://github.com/acme/team-conventions.git enable: - acme-conventionsApply it, work in it, and change whatever turns out to be wrong. Do that before you make a template out of it. A setup that has not survived one real project will be wrong in ways you only find out by using it, and a template makes those mistakes easier to repeat.
Turn it into a template
Section titled “Turn it into a template”Copy the config into your template directory:
cp ~/.monoceros/container-configs/proj-one.yml ~/.monoceros/templates/workbenches/standard.ymlThen open it and change two lines. Where the workbench name is, put
__MONOCEROS_NAME__, and where the runtime version is, put
__MONOCEROS_RUNTIME_VERSION__:
schemaVersion: 1name: __MONOCEROS_NAME__runtimeVersion: __MONOCEROS_RUNTIME_VERSION__
languages: - go:latestThat is the whole format. Everything else stays exactly as it was, comments included, and the rest of the file, the services with their credentials and the features with their options, comes along untouched.
Use it for the next project
Section titled “Use it for the next project”monoceros init proj-two --template=standard --with-repos=https://github.com/acme/web.gitTab completion lists standard next to the four shipped ones.
A service brings its working dev credentials along rather than empty keys, so
Postgres comes up the way it did in the project you built the template from:
GIT_USER_NAME=GIT_USER_EMAIL=CLAUDE_CODE_API_KEY=GITHUB_API_TOKEN=POSTGRES_USER=monocerosPOSTGRES_PASSWORD=monocerosPOSTGRES_DB=monocerosYour own templates are searched before the ones that ship, so naming yours
web-app replaces ours for you. That is deliberate: what we ship is where you
start, and you are meant to outgrow it.
What a template carries that a command cannot
Section titled “What a template carries that a command cannot”Look at the plugins: block on Claude Code above, or the opus and sonnet
on the roles. Neither has a flag, and neither is getting one: a plugin is a nested block with
a marketplace and an enable list, and those model names are options that live in
the yml rather than in the env file. A setup that needs them can only be handed
over as a file.
So whatever you can configure in a workbench yml, you can put in a template: a private plugin marketplace your team uses, a permission mode, the exact Postgres image you have standardised on, your ports.
See also
Section titled “See also”monoceros init- the flags, and the reference for writing your own template.- Add components to a workbench - growing a workbench once it exists.
- From an idea to a built feature - what the
discovery-atlassiantemplate sets you up to do.