This section has been tested for Horizon on Ubuntu (18.04-amd64) and RPM-based (RHEL 8.x) distributions. Feel free to add notes and any changes according to your experiences or operating system.

Linux Systems

Install the prerequisite packages.

On Ubuntu

$ sudo apt install git python3-dev python3-pip gettext

On RPM-based distributions (e.g., Fedora/RHEL/CentOS)

$ sudo yum install gcc git-core python3-devel python3-virtualenv gettext


Some tests rely on the Chrome web browser being installed. While the above requirements will allow you to run and manually test Horizon, you will need to install Chrome to run the full test suite.


To begin setting up a Horizon development environment simply clone the Horizon git repository from https://opendev.org/openstack/horizon.

$ git clone https://opendev.org/openstack/horizon

Next you will need to configure Horizon by adding a local_settings.py file. A good starting point is to use the example config with the following command, from within the horizon directory.

$ cp openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/local_settings.py

Horizon connects to the rest of OpenStack via a Keystone service catalog. By default Horizon looks for an endpoint at http://localhost/identity/v3; this can be customised by modifying the OPENSTACK_HOST and OPENSTACK_KEYSTONE_URL values in openstack_dashboard/local/local_settings.py


The DevStack project (http://devstack.org/) can be used to install an OpenStack development environment from scratch. For a local.conf that enables most services that Horizon supports managing, see DevStack for Horizon

Horizon uses tox to manage virtual environments for testing and other development tasks. You can install it with

$ pip3 install tox

The tox environments provide wrappers around manage.py. For more information on manage.py, which is a Django command, see https://docs.djangoproject.com/en/dev/ref/django-admin/

To start the Horizon development server use the command below

$ tox -e runserver


The default port for runserver is 8000 which might be already consumed by heat-api-cfn in DevStack. If running in DevStack tox -e runserver -- localhost:9000 will start the test server at http://localhost:9000. If you use tox -e runserver for developments, then configure SESSION_ENGINE to django.contrib.sessions.backends.signed_cookies in openstack_dashboard/local/local_settings.py file.

Once the Horizon server is running, point a web browser to http://localhost or to the IP and port the server is listening for. Enter your Keystone credentials, log in and you’ll be presented with the Horizon dashboard. Congratulations!

Managing Settings

You can save changes you made to openstack_dashboard/local/local_settings.py with the following command:

$ python manage.py migrate_settings --gendiff


This creates a local_settings.diff file which is a diff between local_settings.py and local_settings.py.example

If you upgrade Horizon, you might need to update your openstack_dashboard/local/local_settings.py file with new parameters from openstack_dashboard/local/local_settings.py.example to do so, first update Horizon

$ git remote update && git pull --ff-only origin master

Then update your openstack_dashboard/local/local_settings.py file

$ mv openstack_dashboard/local/local_settings.py openstack_dashboard/local/local_settings.py.old
$ python manage.py migrate_settings


This applies openstack_dashboard/local/local_settings.diff on openstack_dashboard/local/local_settings.py.example to regenerate an openstack_dashboard/local/local_settings.py file. The migration can sometimes have difficulties to migrate some settings, if this happens you will be warned with a conflict message pointing to an openstack_dashboard/local/local_settings.py_Some_DateTime.rej file. In this file, you will see the lines which could not be automatically changed and you will have to redo only these few changes manually instead of modifying the full openstack_dashboard/local/local_settings.py.example file.

When all settings have been migrated, it is safe to regenerate a clean diff in order to prevent Conflicts for future migrations

$ mv openstack_dashboard/local/local_settings.diff openstack_dashboard/local/local_settings.diff.old
$ python manage.py migrate_settings --gendiff

Editing Horizon’s Source

Although DevStack installs and configures an instance of Horizon when running stack.sh, the preferred development setup follows the instructions above on the server/VM running DevStack. There are several advantages to maintaining a separate copy of the Horizon repo, rather than editing the DevStack installed copy.

  • Source code changes aren’t as easily lost when running unstack.sh / stack.sh

  • The development server picks up source code changes while still running.

  • Log messages and print statements go directly to the console.

  • Debugging with pdb becomes much simpler to interact with.


To ensure that JS and CSS changes are picked up without a server restart, you can disable compression with COMPRESS_ENABLED = False in your local settings file.

Horizon’s Structure

This project is a bit different from other OpenStack projects in that it has two very distinct components underneath it: horizon, and openstack_dashboard.

The horizon directory holds the generic libraries and components that can be used in any Django project.

The openstack_dashboard directory contains a reference Django project that uses horizon.

If dependencies are added to either horizon or openstack_dashboard, they should be added to requirements.txt.

Project Structure

Dashboard configuration

To add a new dashboard to your project, you need to add a configuration file to openstack_dashboard/local/enabled directory. For more information on this, see Pluggable Panels and Groups.


Then you add a single line to your project’s urls.py

url(r'', include(horizon.urls)),

Those urls are automatically constructed based on the registered Horizon apps. If a different URL structure is desired it can be constructed by hand.


Pre-built template tags generate navigation. In your nav.html template you might have the following

{% load horizon %}

<div class='nav'>
  {% horizon_main_nav %}

And in your sidebar.html you might have

{% load horizon %}

<div class='sidebar'>
  {% horizon_dashboard_nav %}

These template tags are aware of the current “active” dashboard and panel via template context variables and will render accordingly.

Application Design


An application would have the following structure (we’ll use project as an example)

|---dashboard.py <-----Registers the app with Horizon and sets dashboard properties
    |-- images
    |-- __init__.py
    |---panel.py <-----Registers the panel in the app and defines panel properties
    |-- snapshots/
    |-- templates/
    |-- tests.py
    |-- urls.py
    |-- views.py

Dashboard Classes

Inside of dashboard.py you would have a class definition and the registration process

import horizon

# ObjectStorePanels is an example for a PanelGroup
# for panel classes in general, see below
class ObjectStorePanels(horizon.PanelGroup):
    slug = "object_store"
    name = _("Object Store")
    panels = ('containers',)

class Project(horizon.Dashboard):
    name = _("Project") # Appears in navigation
    slug = "project"    # Appears in URL
    # panels may be strings or refer to classes, such as
    # ObjectStorePanels
    panels = (BasePanels, NetworkPanels, ObjectStorePanels)
    default_panel = 'overview'


Panel Classes

To connect a Panel with a Dashboard class you register it in a panel.py file

import horizon

from openstack_dashboard.dashboards.project import dashboard

class Images(horizon.Panel):
    name = "Images"
    slug = 'images'
    permissions = ('openstack.roles.admin', 'openstack.service.image')
    policy_rules = (('endpoint', 'endpoint:rule'),)

# You could also register your panel with another application's dashboard

By default a Panel class looks for a urls.py file in the same directory as panel.py to include in the rollup of url patterns from panels to dashboards to Horizon, resulting in a wholly extensible, configurable URL structure.

Policy rules are defined in horizon/openstack_dashboard/conf/. Permissions are inherited from Keystone and take either the form ‘openstack.roles.role_name’ or ‘openstack.services.service_name’ for the user’s roles in keystone and the services in their service catalog.