Getting started with Juju

This guide introduces you to Juju on MS Windows, macOS and Linux. You’ll be deploying a simple web application to your local machine. Later on, you’ll be able to use those same commands to deploy the app into the cloud, whether private or public.

Watch this guide

Would you prefer to watch, rather than type? This recording goes through the entire tutorial:


Setting up

You need to install snap, which will allow you to install other software used in this tutorial (and cleanly remove things later if desired).

The instructions in this tutorial are based on an Ubuntu system. We’ve provided instructions for getting access to Ubuntu quickly for those readers that require it.

Instructions for MS Windows and macOS

We recommend using an isolated environment for testing.

Install Multipass

If you’re not running Ubuntu, visit to install Multipass. Multipass is a tool for quickly running virtual machines from any host operating system. This will allow you to create a fully-isolated test environment that won’t impact your host system.

Multipass provides a command line interface to launch, manage and generally fiddle about with instances of Linux. The downloading of a minty-fresh image takes a matter of seconds, and within minutes a VM can be up and running.
The Register

Create virtual machine

We want to be able to experiment with Juju and evaluate it without the testing impacting on the rest of our system. To enter a shell within a virtual machine microcloud that has 8 GB RAM allocated to it, execute this command:

$ multipass launch -n microcloud -m 8g -c 2 -d 20G 
Launched: microcloud

Once multipass has downloaded the latest Long Term Support version of the Ubuntu operating system, you will be able to enter a command-line shell:

$ multipass shell microcloud
Need to install snap?

Visit the snapcraft homepage for installation instructions. Snap makes software installation trivial and secure.

Install Juju

Install Juju locally with snap:

$ sudo snap install juju --classic
juju 2.6.8 from Canonical✓ installed

Create a cloud on localhost

LXD manages operating system containers and makes the the “localhost” cloud available to Juju.

LXD is a next generation system container manager. It offers a user experience similar to virtual machines but using Linux containers instead.

It’s image based with pre-made images available for a wide number of Linux distributions
and is built around a very powerful, yet pretty simple, REST API.

LXD website

Ensure LXD is installed

Install LXD with snap:

$ sudo snap install lxd
lxd 3.17 from Canonical✓ installed

If LXD is already installed, you’ll receive this warning:

snap "lxd" is already installed, see 'snap help refresh'

Configure LXD

LXD needs to be configured for its environment:

$ lxd init --auto

If you omit the --auto option, you can tailor your LXD installation. Use lxd help init for a list of all the options.

Verify that the localhost cloud is available

Our localhost cloud is now established. We can verify that by running juju clouds. Juju should have detected the presence of LXD and has added it as the localhost cloud.

$ juju clouds
localhost             1  localhost        lxd         LXD Container Hypervisor

Bootstrap the controller

Juju uses an active software agent, called the controller, to manage applications. The controller is installed on a machine through the bootstrap process.

During the bootstrap process, Juju connects with the cloud, then provision a machine to install the controller on, then install it.

$ juju bootstrap localhost overlord
Creating Juju controller "overlord" on localhost/localhost
Looking for packaged Juju agent version 2.6.8 for amd64

Accessing help: The juju bootstrap command takes several parameters. Use juju help bootstrap to review them.

First workload: Hello Juju!

The first workload that you’ll deploy is a simple web application. You’ll deploy an application that uses the Flask microframework to send “Hello Juju!” via HTTP.

$ juju deploy hello-juju
Located charm "cs:hello-juju-4".
Deploying charm "cs:hello-juju-4".

The charm name hello-juju is resolved into an actual charm version by contacting the Charm Store. This charm is then downloaded by the controller and used as the source of the application that was created with the same name.

Checking the deployment

Now that a workload is in place, use juju status to inspect what is happening:

$ juju status
Model    Controller  Cloud/Region         Version  SLA          Timestamp
default  overlord    localhost/localhost  2.6.8    unsupported  16:24:04+12:00

App         Version  Status  Scale  Charm       Store       Rev  OS      Notes
hello-juju           active      1  hello-juju  jujucharms    4  ubuntu  
Unit           Workload  Agent  Machine  Public address  Ports   Message
hello-juju/0*  active    idle   0   80/tcp  
Machine  State    DNS            Inst id        Series  AZ  Message
0        started  juju-646ac9-0  bionic      Running

Connecting to the application

The juju status output above provided the “Public address” of the hello-juju/0 unit as and its Ports column as 80/tcp. Let’s connect!

$ curl
Hello Juju!
Connection refused?

On non-localhost clouds, the connection may be refused. This is normal. To configure the necessary security groups and firewall rules, use juju expose:

juju expose hello-juju

Access a secure shell

Juju provides some useful functionality for SREs and operations teams who need to interact with units and machines out of the box.

The ssh command accepts unit (and machine) names. They will be substituted for the relevant IP address.

Access the machine via the unit name:

$ juju ssh hello-juju/0

Or via the machine ID:

$ juju ssh 0

Once you’ve connected, you can access the hello-juju web server:

$ curl localhost
Hello Juju!

Relating another application: adding PostgreSQL to the deployment

Relations are Juju’s defining concept and its main point of difference with other systems in its class. They enable the simplicity, security and stability offered by the whole project.

The hello-juju web server maintains a count for each greeting that it has sent out via the /greetings endpoint.

$ curl
{"greetings": 2}

By default, this state is maintained within a SQLite database that is set up by the hello-juju charm itself. To move to PostgreSQL as the data store, two commands are required:

$ juju deploy postgresql
Located charm "cs:postgresql-199".
Deploying charm "cs:postgresql-199".
$ juju relate postgresql:db hello-juju

About relations and how they work

hello-juju includes support for the pgsql relation, which is provided by the postgresql charm and others that present the same interface, such as pg_bouncer.

Relations are protocols that enable applications to auto-configure themselves. In the case of pgsql, the requiring charm (hello-juju) requests that a database be created on its behalf and the provider charm (postgresql) carries that step out and also creates the user account. The postgresql then sends the connection details back to hello-juju.

The data sent between charms is facilitated by the Juju controller. The Juju controller establishes a certificate authority and ensures that all data transported is fully encrypted. Relations prevent secrets from being stored insecurely because they’re not needed by application developers.

What relations are not

Relations are not a networking construct, such as a tunnel between two applications. Instead, they’re a data sharing protocol defined for charm to charm negotiation. As charms are executed by Juju agents, data is shared between agent to agent.

Re-checking the deployment status

Now that the new application and a relation are in place, the juju status output has expanded. Add the --relations option include relations infomation.

$ juju status --relations
Model    Controller  Cloud/Region         Version  SLA          Timestamp
default  overlord    localhost/localhost  2.6.8    unsupported  16:24:04+12:00

App         Version  Status  Scale  Charm       Store       Rev  OS      Notes
hello-juju           active      1  hello-juju  jujucharms    3  ubuntu  
postgresql  10.10    active      1  postgresql  jujucharms  199  ubuntu
Unit           Workload  Agent  Machine  Public address  Ports    Message
hello-juju/0*  active    idle   0   80/tcp  
postgresql/0*  active    idle   0   5432/tcp  Live master (10.10)
Machine  State    DNS            Inst id        Series  AZ  Message
0        started  juju-646ac9-0  bionic      Running
1        started  juju-646ac9-1  bionic      Running

Relation provider       Requirer                Interface    Type     Message
postgresql:coordinator  postgresql:coordinator  coordinator  peer
postgresql:db           hello-juju:db           pgsql        regular
postgresql:replication  postgresql:replication  pgpeer       peer

Next steps

@nottrobin any suggestions for embedding JS? I would like to include the following external resources in this page ideally:

<script id="asciicast-267811" src="" async></script>
<script src=""></script>

Both scripts have been added the discourse theme. You should be able to use either the cards or asciinema now.

Hi timclicks!

Thanks for writing this introduction to Juju!

The second paragraph of the “About relations and how they work” section seems to be truncated. It currently ends “postgresql then sends the”…

Also, might it be worth extending this tutorial a little to explain how to cleanly destroy / “undeploy” that which has been deployed in the guide?


Sorry about that, I’ve finished the sentence.

That’s a good point. I’ll add that to today’s TODO list.

I’m curious if you could extend this “hello-juju” and bring in interfaces for “http/website”? This would enable a continued example also with TLS/SSL - perhaps with ssl-termination-proxy or nginx or apache2 - this is as for most real world scenarios nowdays needs to implement this to be more than just a development example.

Bring in some letsencrypt into the equation and it will be awesome =D

Really good idea. Adding a relation on the provides side will be useful also.

1 Like

The relations, interfaces, endpoints is still much of a mystery to me and the documentation on the topic is also tough.

Much could be done here to simplify…

Very well done, thank you !
If I’m not wrong:

$ juju ssh hello-unit/0

should be:

$ juju ssh hello-juju/0


You’re very right. I’ll make the change now. [Edit: done!]

Thank you for contributing. Welcome to the community!

1 Like

if you have ufw enabled, then containers may not be able to communicate with lxd’s api.

In order to allow the containers to the lxd host use:

sudo ufw allow in on $(lxc network list --format csv | grep ,YES, | tr ',' '\n' | head -n 1)
1 Like

That’s a very useful contribution. Thanks for taking the time to to mention it.

Are we supposed to run this command lxd init --auto as root or normal user?
Running the command using normal user, fails due to permission error.