Devstack set up on the Virtualbox VM

This tutorial will show you how to set up and deploy a simple OpenStack based local cloud. Thanks to the automation you will be able to create and destroy your cloud with minimal effort.

TLDR; Clone, execute the vagrant up command, wait until it finishes and open in your browser.


Here is a list of tools which will let you create a repeatable configuration:

Don't worry if you are not familiar will all of them, this shouldn't be a show stopper as I will do my best to describe everything in a step by step instructions. Those tools will be used to deploy DevStack (OpenStack development environment) locally without touching your local system configuration.

Virtualbox installation

Install Virtualbox for your system using the official download available on As for now, this would be a 6.1.16 version of the Oracle Virtualbox. It will be required later on to easily create and destroy virtual machines (VMs).

Ubuntu Server Cloud Image

If you don't want to manually install Ubuntu Server each time a new VM is created then Ubuntu Server Cloud Images seems to be a pretty good choice. Current LTS Ubuntu Server is named "Focal Fossa", hence the download directory link and an image file itself Please note that .box is the extension for Virtualbox VM images. This image download link will be used later on in a Vagrant configuration, so as for now there is nothing you have to do.

Vagrant setup

Vagrant - this cool automation tool will let you define the exact configuration to spin up the OpenStack VM. As usually the official download page is a good source for the Vagrant installation package. After installation is done you should prepare a directory where you place files used in this project, it can be anywhere in your filesystem e.g. ~/devstack/. Inside this folder create a file named Vagrantfile which will contain a Virtualbox VM configuration. Here is the code that you can copy/paste:

Vagrant.configure("2") do |config|
  # Give a custom name for a VM created by this script for a Vagrant CLI
  config.vm.define "devstack-vm"

  # Name for the box image downloaded from the box_url
  # it will be used to create a folder inside ~/.vagrant.d/boxes to avoid re-downloading = "focal-server-cloudimg-amd64-vagrant"

  # URL used as a source for the defined above
  config.vm.box_url = ""

  # Create additional network interface to make this VM available on the fixed IP address
  # NOTE: I assume that IP is not used in your network "private_network", ip: "", nic_type: "virtio"

  config.vm.provider "virtualbox" do |v|
    # Name visible inside your Virtualbox UI = "devstack-vm"
    # Amount of memory assigned for your VM
    v.memory = 8192 # <--- Adjust this number according to your needs
    # Number of CPUs assigned for your VM
    v.cpus = 4 # <--- Adjust this number according to your needs

    # Enable nested virtualization, so it will be possible to run VMs inside your VM
    v.customize ["modifyvm", :id, "--nested-hw-virt", "on"]
    # Enable promiscuous mode for the network interface number "2"
    v.customize ["modifyvm", :id, "--nicpromisc2", "allow-all"]

  # Enable provisioning with Ansible
  config.vm.provision "ansible" do |ansible|
    # Name of the file with Ansible Playbook definition
    ansible.playbook = "install-devstack.yml"

What is "provisioning" in Vagrant? This is a step after vagrant up completion when a set of tasks/commands is executed on the newly created VM. It allows for VM pre-configuration/setup without a need to execute commands by hand. Another option to trigger the provisioning stage is executing vagrant provision. As you can see Ansible is used as a provisioning tool and a file install-devstack.yml will contain the initial setup, so-called Playbook.

Why the default/primary/first network interface is not reconfigured and a new is added instead? The default Vagrant VM interface is connected to the local NAT which lets you easily connect with the VM through the vagrant ssh command without any further modifications. It is far easier and safer (check the Sources section at the bottom of this post) to add a new network interface to change the default one that is why it is left alone.

Ansible Playbook definition

After your Vagrantfile is ready it is time to write the Ansible Playbook defined in the ansible.playbook = "install-devstack.yml" line. Ideally, Ansible Playbooks should be idempotent. It means that no matter how many times you will execute the given Playbook final VM configuration should remain the same. Your Playbook file should be located in the same directory as your Vagrantfile.

Here is the code for install-devstack.yml ready to be copy/pasted:

- name: VM initialization
  hosts: all
  become: yes
  become_user: root
    - name: Add the 'stack' user for devstack
        name: stack
        comment: Dev stack user
        shell: /bin/bash
        # Definition of a custom user HOME directory, according to the DevStack recommendation
        home: /opt/stack

    - name: Make 'stack' a privileged user
        content: "stack ALL=(ALL) NOPASSWD: ALL"
        dest: /etc/sudoers.d/stack
        mode: 0440

    # Without the `acl` package you may (and probably will) experience permission related issues
    - name: Install acl to fix unprivileged user error
        name: acl
        state: present

    # DevStack scripts uses `python` directly to system has to ensure `python` command availability and map it to the Python 3
    - name: Install python-is-python3 to ensures that python means python3
        name: python-is-python3
        state: present

- name: DevStack setup as 'stack' user
  hosts: all
  become: yes
  become_user: stack
    - name: Clone devstack
        dest: /opt/stack/devstack
        version: stable/victoria

    - name: Copy file with configuration
        src: ./devstack/local.conf
        dest: /opt/stack/devstack/local.conf

    - name: Test if DevStack is installed by checking /opt/stack/tempest existence
        # According to the
        # /opt/stack/tempest dir should exist after devstack installation is completed
        # this is not an ideal way of testing but at least it is something
        cmd: 'test -d "/opt/stack/tempest" && echo "yes" || echo "no"'
      # Save command result to Ansible variable
      register: is_devstack_installed
      ignore_errors: true

    - debug: var=is_devstack_installed

    - name: Install devstack, this step may take up to 30 minutes or even more
      # Use Ansible variable to check if installation should be executed
      when: is_devstack_installed.stdout == "no"
        chdir: /opt/stack/devstack
        cmd: ./
      register: devstack_output

    - debug: var=devstack_output.stdout_lines

As you can see in the Playbook source code an additional file local.conf with a configuration is required and it should be placed inside devstack. The devstack directory should be created inside a folder where the install-devstack.yml file is located.

Here is the content for devstack/local.conf:


Every password is set to be the same as ADMIN_PASSWORD which is set to secret and the IP is used for the HOST_IP value. HOST_IP is used during the DevStack set up process, so in order to change it after DevStack installation, you will have to manually re-trigger the installation process.

Why there is the "Test if DevStack is installed..." step? It is here to avoid a time consuming DevStack installation on each vagrant provision execution. The validation step itself is not anything official or very reliable, so if you are experiencing some issues with that step you can disable it.

Initial DevStack run

Now both Vagrant and Ansible are properly set, so it is very easy to run your local cloud on the OpenStack. If you have followed the previous steps I expect you to have somewhere in your file system a directory with the following files:


The only thing you need to do now is opening a terminal inside this directory and execute the vagrant up command. If for some reason you don't manage to finish the Ansible provisioning step it should be safe to execute vagrant provision again to re-provision the VM. After a successful finish of the Ansible provisioning, you should see the complete output created after Devstack installation. The OpenStack dashboard (called Horizon) should be available on unless you have changed the IP address in the Vagrantfile and HOST_IP inside devstack/local.conf file.

To enter the administrator panel use:

Login: admin Password: secret

After you are done playing with your local OpenStack instance you can easily shut it down with the vagrant halt command or destroy/remove using vagrant destroy. The difference between halt and destroy is that the latter frees all used resources, so you will lose all manual changes you have introduced (it is like "format C" on Windows). On the other hand halt preserves all the data but the disk space will not be freed, the upside is that the next time you execute vagrant up your VM should be exactly in a state in which you have "halted" it. Another important/useful command is vagrant ssh which lets you enter your VM which is running OpenStack, it is especially useful if you need to get the IP address of your VM after restarting it with halt and up commands. The current IP address is quickly accessible after vagrant ssh on the system banner displayed in the console output together with some more details regarding running VM configuration.

Bridged network interface configuration


This is just an alternative setup. You don't have to follow these instructions unless you know this is something that you need.

I believe that for various reasons you may prefer exposing an OpenStack instance as a separate machine in your local network. If that is the case please find out the right IP address first which will be appropriate for your LAN network. It may look as follows provided that your local network is using the – address range (like in my case).

You can always check if the chosen address is free to use by executing ping and checking the results:

PING ( 56 data bytes
Request timeout for icmp_seq 0
Request timeout for icmp_seq 1

Something similar to this will confirm that the IP is not used by any host in your network (in most cases).

As you now have the IP address you should now change the Vagrantfile by replacing: "private_network", ip: "", nic_type: "virtio"

with: "public_network", bridge: "en0: Wi-Fi (AirPort)", ip: "", nic_type: "virtio", name: "vboxnet2"

Of course, this change will require you to destroy your current VM and recreate it from scratch. Please remember to change the HOST_IP value inside devstack/local.conf as well to the IP you have chosen.

What is the "bridged connection" in Virtualbox? This kind of setup makes your VM visible inside your LAN network as a separate machine with a separate IP address. From the network perspective, your VM will look like a separate device. nic_type: "virtio" defines performance optimized network virtualization.

Sources - Vagrant box files location - Virtualbox CLI reference - Ansible modules reference - ensure that python will be an alias for python3 - fix for Failed to set permissions on the temporary files Ansible needs to create when becoming an unprivileged user - OpenStack official page - DevStack installation guide - Ubuntu Cloud Images source - More info about network hardware virtualization - a bit legacy but still useful tutorial on the OpenStack setup - the default Vagrant network interface should not be changed from NAT