Get started on a localhost

Note: This doc has been deprecated. Please see instead: Get started with Juju.

This guide introduces you to Juju and charmed operators deployment on a local Ubuntu machine. If you are using MS Windows or macOS you can still follow this tutorial by installing Multipass. By the end of this guide you will have a running web application backed by a database.


Create the test environment

The instructions in this tutorial are based on an Ubuntu system. This section details how to quickly create a test environment on MS Windows and macOS using a virtual machine deployed by Multipass.

This section is also useful for those that are already running Ubuntu, but would like to create an isolated test environment that could be easily removed.

Info: 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.

Install Multipass

You can find system specific information on how to install Multipass at

Create a virtual machine

To start a virtual machine called, for example, “microcloud”, that has 8 GB RAM allocated to it, execute:

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

Multipass will confirm the creation:

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 with the shell command:

multipass shell microcloud

This gives you access to the shell (you may see a different prompt, depending on the version installed):


You are now ready to follow the rest of this guide in our newly created Ubuntu machine. All commands should be typed in this shell.

If for whatever reason you need to interrupt this tutorial,we recommend running multipass stop microcloud to stop the instance. When you resume, run multipass start microcloud.

Install the Juju CLI client

We will now install the Juju client via snap: this is the easiest and fastest way to get started with Juju.

Other installation methods You can find a comprehensive list of all the ways you have to install Juju at Juju | How to install `juju` .

The following command will install the Juju client:

sudo snap install juju --classic

If the installation was successful, you will see a message similar to the one below:

juju 2.8.10 from Canonical✓ installed

Create a local cloud

We will use LXD for creating a cloud on the localhost. LXD should be already installed in your Ubuntu system. (See How to set up a LXD cloud for a more detailed how-to guide.)

Info: LXD is a system container and virtual machine manager. Juju uses it to instantiate the set of containers that are needed for the app. Need to install LXD? Visit the LXD docs for installation instructions.

Configure LXD

Juju speaks directly to the local LXD daemon, which also requires lxd group membership.

newgrp lxd
sudo adduser $USER lxd

The Ubuntu user is already a member of lxd group.

LXD then needs to be configured for its environment:

lxd init --auto

Finally, IPv6 needs to be disabled:

lxc network set lxdbr0 ipv6.address none

Verify that the localhost cloud is available

Our localhost cloud is now established. We can verify that by running juju clouds:

juju clouds

Juju should have detected the presence of LXD and added it as the localhost cloud:

localhost             1  localhost        lxd         LXD Container Hypervisor

Bootstrap the Juju OLM controller into the cloud

Juju uses an active software agent, called the controller, to manage applications. The controller is installed on a machine through the bootstrap process. The code below illustrates this for a controller called “overlord”.

juju bootstrap localhost overlord

During the bootstrap process, Juju connects with the cloud, then provisions a machine on which to install the controller, then installs the controller.

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

Deploy charmed operators

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

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

Located charm "hello-juju-4".
Deploying charm "hello-juju-4".

The next step is to use juju expose to configure the necessary security groups and firewall rules and make the hello-world application publicly available over the network:

juju expose hello-juju

Check the deployment

Now that a workload is in place, use juju status to inspect what is happening. It can take a few minutes for the application to start up but, once it’s ready, the output will show the status of the hello-juju app as active:

juju status

It can take a few minutes for the application to start up, but once it’s ready, the output will show that the status of the hello-juju app is active:

Model    Controller  Cloud/Region         Version  SLA          Timestamp
default  overlord    localhost/localhost  2.8.10   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

Connect to the application

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


If the connection was successful, you will (among other things) see:

Hello Juju!

Relate charmed operators

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.


Which outputs:

{"greetings": 2}

By default, this state is maintained within a SQLite database that is set up by the hello-juju charmed operator itself. In this section, we will deploy a PostgreSQL database and relate it to our hello-juju charmed operator.

To deploy the PostgreSQL database, we will use the same deploy command:

juju deploy postgresql

Which outputs:

Located charm "postgresql-199".
Deploying charm "postgresql-199".

The PostgreSQL charm may take a few moments to be deployed. You can check the status of the deployment by running:

juju status

When ready, the field status will ready active. You should wait for the charm deployment to finish before following the next steps.

To relate the two charmed operators, all we need to do is run:

juju relate postgresql:db hello-juju

The applications will auto-configure themselves. You now have a web application and a database deployed and ready to use without having to deal with application-specific configuration!

Recheck the deployment status

With another application and a relation in place, the juju status output has expanded:

juju status --relations

Add the --relations option to include relations information:

Model    Controller  Cloud/Region         Version  SLA          Timestamp
default  overlord    localhost/localhost  2.8.10   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

Destroy your test environment

Once you are done, you can run multipass delete microcloud to delete your instance. You can also uninstall Multipass to remove any trace of this guide.

Next steps

This tutorial has introduced you to the basic things you can do with Juju. Visit the Juju How-to docs to experiment with further features such as scaling or cross-model relations; the Juju Reference docs to learn more about the primitive notions and commands that make Juju work; or the Juju Explanation docs, just for fun.

@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.

The document is missing the following command after lxd init --auto:

lxc network set lxdbr0 ipv6.address none

This is mentioned in the ascii capture but not in the doc. As a result:

$ juju bootstrap localhost overlord
ERROR profile "default": juju does not support IPv6. Disable IPv6 in LXD via:
	lxc network set lxdbr0 ipv6.address none
and run the command again
1 Like

I’d like to propose some updates to this documentation, having followed its steps from beginning to end.

The prompt I get is:


I suggest for this last line:

It can take a few minutes for the application to start up, but once it’s ready, the output will show that the status of the hello-juju app is active:

2 Likes link is broken

1 Like

I would like to suggest some updates to this documentation.

  1. In Create a virtual machine section, it should mention that microcloud is just an optional name for the created instance and a different name can be used based on user’s preference. Similarly, in Bootstrap the Juju OLM controller section, it should mention that overlord is an optional name for the controller.
  2. As mentioned in one of the previous comments, a step for disabling IPv6 is still missing in the documentation. The following command line needs to be added after lxd init --auto :
    lxc network set lxdbr0 ipv6.address none
  3. The format of the section title Check the deployment is incorrect.
1 Like

multipass destroy does not exist, managed to cleanup with:

multipass delete <instance> ; multipass purge

1 Like