concierge is an opinionated utility for provisioning charm development and testing machines.
Its role is to ensure that a given machine has the relevant "craft" tools and providers installed, then bootstrap a Juju controller onto each of the providers. Additionally, it can install selected tools from the snap store or the Ubuntu archive.
concierge also provides the facility to "restore" a machine, performing the opposite to "prepare"
meaning that, for example, any snaps that would have been installed by concierge, would then be
removed.
Important
Take care with concierge restore. If the machine already had a given snap or configuration
prior to running concierge prepare, this will not be taken into account during restore.
Running concierge restore is the literal opposite of concierge prepare, so any packages,
files or configuration that would normally be created during prepare will be removed.
The easiest way to consume concierge is using the Snap:
sudo snap install --classic conciergeOr, you can install concierge with the go install command:
go install github.com/canonical/concierge@latestOr you can clone, build and run like so:
git clone https://github.com/canonical/concierge
cd concierge
go build
sudo ./concierge -hThe output of concierge --help can be seen below.
concierge is an opinionated utility for provisioning charm development and testing machines.
Its role is to ensure that a given machine has the relevant "craft" tools and providers installed,
then bootstrap a Juju controller onto each of the providers. Additionally, it can install selected
tools from the [snap store](https://snapcraft.io) or the Ubuntu archive.
Usage:
concierge [flags]
concierge [command]
Available Commands:
completion Generate the autocompletion script for the specified shell
help Help about any command
prepare Provision the machine according to the configuration.
restore Run the reverse of `concierge prepare`.
status Report the status of `concierge` on the machine.
Flags:
-h, --help help for concierge
--trace enable trace logging
-v, --verbose enable verbose logging
--version version for concierge
Use "concierge [command] --help" for more information about a command.
Some flags can be set by environment variable, and if specified by flag and environment variable, the environment variable version will always take precedent. The equivalents are:
| Flag | Env Var |
|---|---|
--disable-juju |
CONCIERGE_DISABLE_JUJU |
--juju-channel |
CONCIERGE_JUJU_CHANNEL |
--k8s-channel |
CONCIERGE_K8S_CHANNEL |
--microk8s-channel |
CONCIERGE_MICROK8S_CHANNEL |
--lxd-channel |
CONCIERGE_LXD_CHANNEL |
--charmcraft-channel |
CONCIERGE_CHARMCRAFT_CHANNEL |
--snapcraft-channel |
CONCIERGE_SNAPCRAFT_CHANNEL |
--rockcraft-channel |
CONCIERGE_ROCKCRAFT_CHANNEL |
--google-credential-file |
CONCIERGE_GOOGLE_CREDENTIAL_FILE |
--extra-snaps |
CONCIERGE_EXTRA_SNAPS |
--extra-debs |
CONCIERGE_EXTRA_DEBS |
The best source of examples for how to invoke concierge can be found in the
tests directory, but otherwise:
- Run
conciergeusing thedevpreset, adding one additional snap, using CLI flags:
sudo concierge prepare -p dev --extra-snaps node/22/stable- Run
conciergeusing thedevpreset, overriding the Juju channel:
export CONCIERGE_JUJU_CHANNEL=3.6/beta
sudo concierge prepare -p devBoth prepare and restore commands support a --dry-run flag that shows what
operations would be performed without actually making any changes to the system.
# Preview prepare operations
sudo concierge prepare -p dev --dry-run
# Preview restore operations
sudo concierge restore --dry-runIn dry-run mode:
- Commands are printed to stdout in a format that can be copied and pasted into a shell
- Log level defaults to error (use
--traceor--verboseto see more details) - No packages are installed or removed
- No files are created or modified
- No Juju controllers are bootstrapped or destroyed
- System state is read for accurate conditional logic (for example, checking if snaps are already installed). This means if your configuration references files that don't exist (e.g., Google Cloud credentials), dry-run will fail with the same error that would occur during actual execution
This is useful for verifying what concierge will do before running it, or for
understanding what a particular preset or configuration file includes.
concierge comes with a number of presets that are likely to serve most charm development needs:
| Preset Name | Included |
|---|---|
crafts |
lxd, snapcraft, charmcraft, rockcraft |
dev |
juju, k8s, lxd, snapcraft, charmcraft, rockcraft, jhack |
k8s |
juju, k8s, lxd, rockcraft, charmcraft |
microk8s |
juju, microk8s, lxd, rockcraft, charmcraft |
machine |
juju, lxd, snapcraft, charmcraft |
Note that in the microk8s/k8s presets, while lxd is installed, it is not bootstrapped. It is
installed and initialised with enough config such that charmcraft can use it as a build backend.
Presets are defined as YAML files in the presets/ directory. If you want to create a
custom configuration, a good starting point is to copy the preset that most closely matches your
needs and modify it. For example:
cp $(go env GOPATH)/pkg/mod/github.com/canonical/concierge@latest/presets/dev.yaml concierge.yaml
# Edit concierge.yaml to suit your needs
sudo concierge prepare -c concierge.yamlOr, if you installed concierge from the snap, you can view the preset files on GitHub in the
presets/ directory and use them as a reference.
If the presets do not meet your needs, you can create your own config file to instruct concierge
how to provision your machine.
concierge takes configuration in the form of a YAML file named concierge.yaml in the current
working directory.
# (Optional): Target Juju configuration.
juju:
# (Optional): Disable installation of Juju (and therefore all bootstrapping).
disable: true | false
# (Optional): Channel from which to install Juju.
channel: <channel>
# (Optional): Juju agent version to use when bootstrapping (e.g. "3.6.11").
agent-version: <version>
# (Optional): A map of model-defaults to set when bootstrapping *all* Juju controllers.
model-defaults:
<model-default>: <value>
# (Optional): A map of bootstrap-constraints to set when bootstrapping *all* Juju controllers.
bootstrap-constraints:
<bootstrap-constraint>: <value>
# (Optional): A string of extra arguments to append to the `juju bootstrap` command.
# The string will be parsed using shell-style splitting rules.
extra-bootstrap-args: <args>
# (Required): Define the providers to be installed and bootstrapped.
providers:
# (Optional) MicroK8s provider configuration.
microk8s:
# (Optional) Enable or disable MicroK8s.
enable: true | false
# (Optional) Whether or not to bootstrap a controller onto MicroK8s.
bootstrap: true | false
# (Optional): A map of model-defaults to set when bootstrapping the Juju controller.
model-defaults:
<model-default>: <value>
# (Optional): A map of bootstrap-constraints to set when bootstrapping the Juju controller.
bootstrap-constraints:
<bootstrap-constraint>: <value>
# (Optional): Channel from which to install MicroK8s.
channel: <channel>
# (Optional): MicroK8s addons to enable.
addons:
- <addon>[:<params>]
# (Optional): Configure an image registry mirror (e.g., for Docker Hub).
# Useful in environments with registry access restrictions or rate limits.
# Values support environment variable interpolation (e.g., $VAR or ${VAR}).
image-registry:
# URL of the registry mirror.
url: <url>
# (Optional): Username for registry authentication.
username: <username>
# (Optional): Password for registry authentication.
password: <password>
# (Optional) K8s provider configuration.
k8s:
# (Optional) Enable or disable K8s.
enable: true | false
# (Optional) Whether or not to bootstrap a controller onto K8s.
bootstrap: true | false
# (Optional): Channel from which to install K8s.
channel: <channel>
# (Optional): A map of model-defaults to set when bootstrapping the Juju controller.
model-defaults:
<model-default>: <value>
# (Optional): A map of bootstrap-constraints to set when bootstrapping the Juju controller.
bootstrap-constraints:
<bootstrap-constraint>: <value>
# (Optional): K8s features to configure.
features:
<feature>:
<key>: <value>
# (Optional): Configure an image registry mirror (e.g., for Docker Hub).
# Useful in environments with registry access restrictions or rate limits.
# Values support environment variable interpolation (e.g., $VAR or ${VAR}).
image-registry:
# URL of the registry mirror.
url: <url>
# (Optional): Username for registry authentication.
username: <username>
# (Optional): Password for registry authentication.
password: <password>
# (Optional) LXD provider configuration.
lxd:
# (Optional) Enable or disable LXD.
enable: true | false
# (Optional) Whether or not to bootstrap a controller onto LXD.
bootstrap: true | false
# (Optional): Channel from which to install LXD.
channel: <channel>
# (Optional): A map of model-defaults to set when bootstrapping the Juju controller.
model-defaults:
<model-default>: <value>
# (Optional): A map of bootstrap-constraints to set when bootstrapping the Juju controller.
bootstrap-constraints:
<bootstrap-constraint>: <value>
# (Optional) Google provider configuration.
google:
# (Optional) Enable or disable the Google provider.
enable: true | false
# (Optional) Whether or not to bootstrap a controller onto Google Cloud.
bootstrap: true | false
# (Optional): File containing credentials for Google cloud.
# See below note on the credentials file format.
credentials-file: <path>
# (Optional): A map of model-defaults to set when bootstrapping the Juju controller.
model-defaults:
<model-default>: <value>
# (Optional): A map of bootstrap-constraints to set when bootstrapping the Juju controller.
bootstrap-constraints:
<bootstrap-constraint>: <value>
# (Optional) Additional host configuration.
host:
# (Optional) List of apt packages to install on the host.
packages:
- <package name>
# (Optional) Map of snap packages to install on the host.
snaps:
<snap name>:
# (Optional) Channel from which to install the snap. If omitted, the default behaviour is decided by snapd.
channel: <channel>
# (Optional) List of snap connections to form.
connections:
- <snap>:<plug-interface>
- <snap>:<plug-interface> <snap>:<plug-interface>Juju has some "built-in" clouds for which it can obtain credentials automatically, such as LXD and MicroK8s. Other clouds require credentials for the bootstrap process.
Concierge handles this with the credentials-file option for supported providers.
Juju's credentials are specified in ~/.local/share/juju/credentials.yaml, in the following format:
credentials:
<cloud-name>:
<credential-name>:
key: value
key: valueFor example, a pre-configured credentials.yaml might look like so where Google Cloud had already been configured:
credentials:
google:
mycred:
auth-type: oauth2
client-email: juju-gce-1-sa@myname.iam.gserviceaccount.com
client-id: "1234567891234"
private-key: |
-----BEGIN PRIVATE KEY-----
deadbeef
-----END PRIVATE KEY-----
project-id: foobarWhen providing the path to a credentials-file for concierge, it should contain only the details for a specific credential, so the example above would require a file like so:
auth-type: oauth2
client-email: juju-gce-1-sa@myname.iam.gserviceaccount.com
client-id: "1234567891234"
private-key: |
-----BEGIN PRIVATE KEY-----
deadbeef
-----END PRIVATE KEY-----
project-id: foobarIf you already have a credentials.yaml pre-populated with credentials, you can use yq to build the file for you, for example:
cat ~/.local/share/juju/credentials.yaml | yq -r '.credentials.google.mycred' > google-creds.yamlIn the above example google-creds.yaml would be valid for the credentials-file option.
An example config file can be seen below:
juju:
channel: 3.6/stable
agent-version: "3.6.8"
model-defaults:
test-mode: "true"
automatically-retry-hooks: "false"
bootstrap-constraints:
arch: amd64
extra-bootstrap-args: --config idle-connection-timeout=90s
providers:
microk8s:
enable: true
bootstrap: true
channel: 1.31-strict/stable
addons:
- hostpath-storage
- dns
- rbac
- metallb:10.64.140.43-10.64.140.49
# Configure a Docker Hub mirror using an environment variable
image-registry:
url: $DOCKERHUB_MIRROR
k8s:
enable: true
bootstrap: true
channel: 1.32-classic/stable
features:
local-storage:
load-balancer:
l2-mode: true
cidrs: 10.64.140.43/32
bootstrap-constraints:
root-disk: 2G
# Configure a Docker Hub mirror with authentication
image-registry:
url: https://registry.example.com
username: ${REGISTRY_USER}
password: ${REGISTRY_PASS}
lxd:
enable: true
bootstrap: false
channel: latest/stable
google:
enable: true
bootstrap: false
credentials-file: /home/ubuntu/google-credentials.yaml
host:
packages:
- python3-pip
- python3-venv
snaps:
astral-uv:
channel: latest/stable
charmcraft:
channel: latest/stable
jhack:
channel: latest/edge
connections:
- jhack:dot-local-share-jujuFor information on contributing to and developing Concierge, see CONTRIBUTING.md.
