.. _tutorial_lxd: Quick-start tutorial with LXD ***************************** In this tutorial, we will create our first cloud-init user-data script and deploy it into a `LXD`_ container. Why LXD? ======== We'll be using LXD for this tutorial because it provides first class support for cloud-init user-data, as well as ``systemd`` support. Because it is container based, it allows us to quickly test and iterate upon our user-data definition. Install and initialize LXD ========================== If you already have LXD set up, you can skip this section. Otherwise, let's install LXD: .. code-block:: shell-session $ sudo snap install lxd If you don't have snap, you can install LXD using one of the `other installation options`_. Now we need to initialize LXD. The minimal configuration will be enough for the purposes of this tutorial. If you need to, you can always change the configuration at a later time. .. code-block:: shell-session $ lxd init --minimal Define our user-data ==================== Now that LXD is set up, we can define our user-data. Create a file on your local filesystem at :file:`/tmp/my-user-data` and populate it with this content: .. code-block:: yaml #cloud-config runcmd: - echo 'Hello, World!' > /var/tmp/hello-world.txt Here, we are defining our cloud-init user-data in the :ref:`#cloud-config ` format, using the :ref:`runcmd module ` to define a command to run. When applied, it will write ``Hello, World!`` to :file:`/var/tmp/hello-world.txt` (as we shall see later!). Launch a LXD container with our user-data ========================================= Now that we have LXD set up and our user-data defined, we can launch an instance with our user-data: .. code-block:: shell-session $ lxc launch ubuntu:focal my-test --config=user.user-data="$(cat /tmp/my-user-data)" Verify that cloud-init ran successfully ------------------------------------------- After launching the container, we should be able to connect to our instance using: .. code-block:: shell-session $ lxc shell my-test You should now be in a shell inside the LXD instance. Before validating the user-data, let's wait for cloud-init to complete successfully: .. code-block:: shell-session $ cloud-init status --wait Which provides the following output: .. code-block:: status: done Verify our user-data -------------------- Now we know that cloud-init ran successfully, we can verify that it received the expected user-data we provided earlier: .. code-block:: shell-session $ cloud-init query userdata Which should print the following to the terminal window: .. code-block:: #cloud-config runcmd: - echo 'Hello, World!' > /var/tmp/hello-world.txt We can also assert the user-data we provided is a valid cloud-config: .. code-block:: shell-session $ cloud-init schema --system --annotate Which should print the following: .. code-block:: Valid schema user-data Finally, let us verify that our user-data was applied successfully: .. code-block:: shell-session $ cat /var/tmp/hello-world.txt Which should then print: .. code-block:: Hello, World! We can see that cloud-init has received and consumed our user-data successfully! Completion and next steps ========================= Exit the container shell (by typing :command:`exit` or pressing :kbd:`Ctrl-D`). Once we have exited the container, we can stop the container using: .. code-block:: shell-session $ lxc stop my-test We can then remove the container completely using: .. code-block:: shell-session $ lxc rm my-test In this tutorial, we used the :ref:`runcmd module ` to execute a shell command. The full list of modules available can be found in our :ref:`modules documentation`. Each module contains examples of how to use it. You can also head over to the :ref:`examples page` for examples of more common use cases. .. _LXD: https://ubuntu.com/lxd .. _other installation options: https://documentation.ubuntu.com/lxd/en/latest/installing/#other-installation-options