Build your first charm

Go from zero to a deployed, tested Juju charm in a single sitting. This tutorial takes about 15 minutes.

What you will learn

By the end of this tutorial you will have:

This tutorial uses a simple Flask application as the example workload. Cantrip handles it via Path A (12-factor PaaS), which is the fastest charm path. The same workflow applies to more complex workloads — only the design proposal and build time differ.

Prerequisites

Before you begin, make sure you have:

1. Install Cantrip

Install Cantrip as a standalone tool using uv:

$ uv tool install juju-cantrip

Verify the installation:

$ cantrip --version
cantrip 0.x.y

2. Set up your API key

Export your Gemini API key so Cantrip can access the model:

$ export GEMINI_API_KEY="your-key-here"

Add this to your shell profile (~/.bashrc or ~/.zshrc) so it persists across sessions.

For Claude, Fireworks, OpenRouter, OpenCode Zen, an OpenAI-compatible endpoint, or a local inference snap, see Choose an LLM provider — environment variables for the matching export command. The CLI reference lists every operational variable Cantrip reads.

3. Create a project directory

Cantrip works inside a charm project directory. Create one and move into it:

$ mkdir my-flask-charm && cd my-flask-charm

4. Launch Cantrip

Start Cantrip in the default TUI mode:

$ cantrip

The terminal UI opens with three panels: a task checklist on the left, Juju status in the centre, and a chat panel on the right. Your cursor is in the chat input area, ready for your first message.

If you prefer a browser interface, launch with cantrip --web instead. For a minimal command-line REPL, use cantrip --no-tui.

5. Describe your workload

In the chat panel, type a description of what you want to charm:

Build a charm for a Flask hello-world application

Press Enter. The agent begins its research phase — you will see tasks appearing in the checklist on the left as it searches the web, checks Charmhub for existing charms, and analyses the workload.

6. Review the design proposal

After a few moments the agent presents a structured design proposal in the chat. It looks something like this:

Substrate:      Kubernetes
Charm path:     A (12-Factor PaaS)
Base:           paas-charm (Flask)
Integrations:   ingress, COS
Config:         app-port (default 8080)
Observability:  ops-tracing, Prometheus metrics

The agent waits for your confirmation. Read through the proposal. If it looks right, reply:

Looks good, go ahead

If you want changes, tell the agent what to adjust — for example, "add a database integration" or "use machine substrate instead".

7. Watch the build

Once confirmed, the agent enters the autonomous work loop. Watch the task checklist update as subagents work through the plan:

You do not need to type anything during this phase. The agent handles errors automatically — if a build fails, it diagnoses the issue using traces and logs and creates a fix task.

For a simple Flask charm, the build typically completes in under two minutes. More complex workloads (Path B or C) take longer.

8. Verify the deployment

When all tasks show a tick in the checklist, your charm is deployed and tested. Verify it with Juju:

$ juju status
Model    Controller  Cloud/Region        Version  ...
default  lxd         localhost/localhost  3.x.y    ...

App              Version  Status  Scale  Charm            Channel  Rev
my-flask-charm            active      1  my-flask-charm              0

Unit                  Workload  Agent  Machine  ...
my-flask-charm/0*     active    idle   0        ...

The charm should be in active/idle status. Observability is automatically wired up via COS — traces and metrics are flowing.

Next steps

You have built and deployed your first charm with Cantrip. From here: