Git and repositories
Add a repository URL and your workbench is ready to work. Monoceros clones your repos and signs you in to GitHub, GitLab and Bitbucket, so you can commit and push from inside the container straight away.
All it takes is one personal access token. Set it once and every workbench you build can reach your repositories. The token stays in a private env file.
New to Monoceros? Start with Installation.
Get your repo into a container
Section titled “Get your repo into a container”Three steps take you from nothing to a cloned, authenticated repo inside the container: create a token, store it, then add the repo. The token is a one-time setup - once it’s in place, adding repos just works, at init or later.
1. Create an access token
Section titled “1. Create an access token”A personal access token is how you prove to GitHub, GitLab or Bitbucket that a clone or push is really you. It’s the same token you’d create to use their APIs or command-line tools, and it stands in for a password over HTTPS. Create one for each provider you work with.
GitHub
Section titled “GitHub”- Open github.com/settings/tokens.
- Click Generate new token, then choose Generate new token (classic).
- Give it a note and an expiry, then tick these scopes:
repo,read:org,gist,read:user. - Click Generate token and copy the value - it starts with
ghp_, and GitHub shows it only once.
GitLab
Section titled “GitLab”- Open Personal access tokens in your user settings.
- Click Generate token, then choose the Legacy token option.
- Give it a name and expiry, then tick these scopes:
api,read_repositoryandwrite_repository(dropwrite_repositoryif you only ever clone). - Click Create personal access token and copy the value - it starts with
glpat-, and GitLab shows it only once.
Bitbucket Cloud
Section titled “Bitbucket Cloud”Bitbucket uses an Atlassian API token with scopes. Fastest: this pre-filled link opens the token form with Bitbucket as the app and every scope selected - create it as is and copy the value (Atlassian shows it only once), or step Back to the start to rename or pick fewer scopes.
Which scopes matter: cloning and pushing needs read:repository:bitbucket and
write:repository:bitbucket (drop the write scope if you only clone); twg’s
pull-request, pipeline and workspace commands add read:pullrequest:bitbucket
and write:pullrequest:bitbucket, read:pipeline:bitbucket and
write:pipeline:bitbucket, and read:workspace:bitbucket.
Or step through it by hand:
- Open Atlassian API tokens.
- Click Create API token with scopes.
- Give it a name and expiry, then continue.
- Choose Bitbucket as the app, then continue.
- Tick the scopes from the list above.
- Create the token and copy the value - Atlassian shows it only once.
2. Store the token
Section titled “2. Store the token”Save it in an env file beside your configs, under the variable for its provider.
The simplest place is monoceros-config.env in your Monoceros home - a global
pool every container reads, so you set a token once and it works everywhere:
GITHUB_API_TOKEN=ghp_...GITLAB_API_TOKEN=glpat-...ATLASSIAN_BITBUCKET_TOKEN=...This file holds your secrets, so keep it private - it lives in your Monoceros
home, away from any project repo. For a token that should apply to one
container only, put it in that container’s <name>.env (e.g. acme.env, which
is gitignored) instead; a value there wins over the global pool.
The one token does double duty: Monoceros writes it into the container’s git
credential store (so git clone / push works) and into the provider CLI
feature (so gh / glab is logged in).
It also covers a private plugin marketplace. An agent plugin is cloned inside the container by the same git, so a marketplace on a private repository needs the same token as a private repo, from the same env files. Monoceros says so before it builds anything if the token is missing. See Claude Code.
The yml never holds a secret itself. Wherever it needs a value from the env - a
git identity, a feature’s own token - it carries a ${VAR} placeholder that
Monoceros fills at apply time from these env files (<name>.env first, then the
global pool). So a name like ${GIT_USER_NAME} in the yml is just a lookup into
your env; Configuration covers the yml/env split
in full.
3. Add the repo
Section titled “3. Add the repo”With the token in place, point Monoceros at your repositories.
At init - pass one or more repos (comma-separated) when you create the container:
monoceros init acme \ --with-repos=https://github.com/acme/api.git,https://github.com/acme/web.gitThey’re recorded in the yml and cloned into projects/ on the first apply, one
folder per repo (derived from the URL: api.git → projects/api/).
monoceros init accepts canonical hosts here
(github.com / gitlab.com / bitbucket.org); for a self-hosted host or a custom
target folder, add the repo afterwards (below) and set path / provider in the
yml.
Later, one at a time - monoceros add-repo
adds a single repo to an existing config. The example below adds two: a GitHub
repo, and a self-hosted GitLab repo whose host Monoceros can’t auto-detect, so it
names the provider explicitly.
monoceros add-repo acme https://github.com/acme/api.gitmonoceros add-repo acme https://git.acme.corp/team/web.git --provider=gitlabIf the container is running, the repo is cloned into it right away, using the
token from step 2 - so if that token isn’t set yet, the clone fails with an auth
error. On a running container, create and store the token first, then add-repo.
Either way, the repos end up in the yml under repos:
repos: - url: https://github.com/acme/api.git - url: https://git.acme.corp/team/web.git provider: gitlabgithub.com, gitlab.com and bitbucket.org are detected automatically; for any
other host, add provider (github / gitlab / bitbucket) so Monoceros knows
which token to use. Each repo clones into a folder under projects/ named after
the repo - add path to an entry to choose a different folder. URLs must be
HTTPS, and cloning is idempotent - an existing projects/<path>/ is left
untouched, so your local changes and branches survive a monoceros apply
rebuild. On apply, Monoceros reports which token it used for each repo:
Using GITHUB_API_TOKEN for github.comStart a new project
Section titled “Start a new project”Not every workbench starts from an existing repo. Often you’re building something new: you scaffold a project inside the container, and then you want to create the remote, push it, and open pull requests without ever leaving it. Add the provider CLI on its own - no repo required:
monoceros init acme --with-features=githubmonoceros init acme --with-features=gitlabThe first gives you gh, the second glab; add --with-features=github,gitlab
for both. With the token from step 2 in place, they’re already logged in, so from
inside the container you go straight from a fresh project to a pushed repo. With
gh:
gh repo create acme/new-app --private --source=. --pushgh pr createOr with glab:
glab repo create new-appglab mr createIt’s the same token from step 2 doing its job, no repo needed. Once you apply,
gh and glab are authenticated inside the container, so creating the remote and
pushing your first commit is a one-liner away.
Recipes
Section titled “Recipes”Several accounts on one machine
Section titled “Several accounts on one machine”Working across a work org and a personal account? Give each its own token in the
global pool, named GIT_TOKEN__<PROVIDER>_<ACCOUNT> - here a work org (ACME)
and a personal account (JANE):
GIT_TOKEN__GITHUB_ACME=ghp_...GIT_TOKEN__GITHUB_JANE=ghp_...GIT_TOKEN__GITLAB_ACME=glpat-...GITHUB_API_TOKEN=${GIT_TOKEN__GITHUB_ACME}Monoceros keys each repo to the right token by the first path segment of its
URL - the GitHub owner or organization, or the GitLab group (uppercased,
non-alphanumerics turned to _). So github.com/acme/api picks
GIT_TOKEN__GITHUB_ACME and a repo under your personal namespace picks
GIT_TOKEN__GITHUB_JANE, straight from the URL - no per-container setup.
To pin one container to a specific account regardless of the repo URL, reference
the pooled token from its <name>.env (the acme.env tab above) - it doesn’t
copy the secret, just points at it.
Under the hood that’s a cascade; for each repo apply takes the first var that’s
set:
<PROVIDER>_API_TOKEN- the explicit override (also settable per container in<name>.env).GIT_TOKEN__<PROVIDER>_<SEGMENT>- the account-keyed token above.GIT_TOKEN__<PROVIDER>- a provider-wide catch-all.
This cascade is for GitHub and GitLab. Bitbucket authenticates from the single
ATLASSIAN_BITBUCKET_TOKEN (its token is user-scoped, so it isn’t keyed per
workspace) - see the Atlassian feature.
Self-hosted GitLab
Section titled “Self-hosted GitLab”For a host Monoceros doesn’t recognise, declare the provider and key a token to
its group segment (or use the provider-wide GIT_TOKEN__GITLAB):
repos: - url: https://git.acme.corp/team/web.git provider: gitlabGIT_TOKEN__GITLAB_TEAM=glpat-...Commit identity
Section titled “Commit identity”Commits made inside the container need a name and email. Monoceros resolves them as a cascade, most specific first:
- Per-repo -
repos[].git.useroverrides the identity for that one repo. Useful when one workbench holds a work repo and a personal repo that need different committers. - Container-level - the top-level
git.userapplies to every repo in the workbench. Use${VAR}references so the values come fromacme.env, not the shareable yml. - Your env -
GIT_USER_NAMEandGIT_USER_EMAILinmonoceros-config.env, or in a container’s ownacme.env. No yml entry is needed for these to apply. - Machine-wide default -
defaults.git.userinmonoceros-config.ymlapplies to every container you build, and takes precedence over your host git config. - Host fallback - if none of those is set,
applyreads your host’sgit config --global user.name/user.email. - One-time prompt - if even that is empty,
applyasks once and writes the answer to the env file you pick, so later applies and new containers don’t prompt again.
Set it once in the env, and every container you build from then on has it:
GIT_USER_NAME=Jane DoeGIT_USER_EMAIL=jane@example.comThat file is the right home for it: a name and an address are personal data, the
env is gitignored, and the yml is the part you can share. monoceros check tells
you when a container has no identity, because the failure otherwise shows up as
“Please tell me who you are” on your first commit inside it.
Every step in that list is a file you can edit, and that is the whole point:
remove the identity from where you put it, apply, and the container no longer
has one. The .monoceros/gitconfig inside the container is written by apply
and never read back as a source. If it were, an identity would outlive the thing
that declared it.
A container - or a single repo within it - can still override that. The yml reads
its committer from ${GIT_USER_NAME} / ${GIT_USER_EMAIL} in acme.env (init
seeds those keys for you to fill in):
git: user: name: ${GIT_USER_NAME} email: ${GIT_USER_EMAIL}
repos: - url: https://github.com/acme/api.git git: user: name: Jane Work email: jane@acme.exampleGIT_USER_NAME=Jane DoeGIT_USER_EMAIL=jane@example.comSo this container commits as Jane Doe by default, while the api repo overrides
that to commit as Jane Work.
See also
Section titled “See also”monoceros add-repo/monoceros remove-repo- manage repos.- GitHub CLI / GitLab CLI -
the
gh/glabfeatures and theirapiTokenoption. - Workbench configuration - the
reposandgitfields, and the.envsplit, in full.