Networking Azure

OpenWISP Controller on Ubuntu 24.04 on Azure User Guide

| Product: OpenWISP Controller on Ubuntu 24.04 LTS on Azure

Overview

This image runs OpenWISP Controller 1.2.3, the flagship module of OpenWISP, the open source network management system, on Ubuntu 24.04 LTS. OpenWISP Controller is a centralised, multi tenant platform for provisioning and managing the configuration of network devices such as routers and wireless access points. You define reusable configuration templates, apply them to devices with automatic per device variable substitution, and the controller compiles and serves each device its exact configuration. It also provides an integrated x509 PKI for certificate authorities and certificates, VPN provisioning for OpenVPN, WireGuard, ZeroTier and VXLAN over WireGuard, device connection and credential management, automatic subnet division and IP address management, and a complete REST API for automation.

cloudimg delivers OpenWISP Controller fully installed and reverse proxied behind nginx, with the gunicorn application server, a Celery worker and scheduler, PostgreSQL 16 with the PostGIS spatial extension and Redis 7 already configured and managed by systemd, so a working controller is operational within minutes of launch.

What is included:

  • OpenWISP Controller 1.2.3 installed from PyPI into a dedicated Python virtual environment at /opt/openwisp
  • PostgreSQL 16 + PostGIS 3 as the primary spatial database and Redis 7 as the Celery broker, cache and channels layer
  • gunicorn serving the Django application on 127.0.0.1:8001, fronted by nginx on port 80
  • A Celery worker (openwisp-celery.service) and a Celery beat scheduler (openwisp-celery-beat.service) for background and periodic controller tasks
  • Six systemd units enabled and active: postgresql, redis-server, openwisp, openwisp-celery, openwisp-celery-beat and nginx
  • Secure by default: no administrator account and no shared secret ship in the image. On first boot a unique Django secret key, a unique database password, a unique administrator account and a REST API token are generated and written to a root only file
  • A fully patched Ubuntu 24.04 LTS base with a swapfile for the recommended size and unattended security upgrades enabled
  • 24/7 cloudimg support

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is the recommended size for evaluation and small fleets; for larger fleets or when you add the OpenWISP monitoring module, choose a size with more memory. NSG inbound: allow 22/tcp from your management network and 80/tcp for the admin interface. OpenWISP serves plain HTTP on port 80; for production or shared access, terminate TLS in front of it with your own domain (see the final step).

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for OpenWISP by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create then Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group your-rg \
  --name openwisp \
  --image marketplace-image-urn \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name your-vnet --subnet your-subnet \
  --public-ip-sku Standard

# Open the HTTP port so you can reach the admin interface
az vm open-port --resource-group your-rg --name openwisp --port 80 --priority 900

Step 3 - Retrieve the per VM admin credentials

Nothing secret is baked into the image. On the very first boot, a one shot service (openwisp-firstboot.service) generates a fresh Django secret key, a fresh database password, a unique administrator account and a REST API token, all unique to your instance. It writes them to /root/openwisp-credentials.txt, readable only by root. SSH in as azureuser and read them:

$ sudo cat /root/openwisp-credentials.txt
# OpenWISP Controller - generated on first boot by openwisp-firstboot.service.
# These credentials are unique to THIS VM. Store them somewhere safe.

openwisp.url=http://20.55.10.42/admin/
openwisp.admin.user=admin
openwisp.admin.pass=Xq7pR2mK9wU4nZ8vB3hL6yD1
openwisp.api.token=8f3c1e0a9b2d4c6e8f0a1b2c3d4e5f60718293a4

# Canonical keys consumed by the cloudimg smoke verifier + guide substitution:
OPENWISP_ADMIN_PASSWORD=Xq7pR2mK9wU4nZ8vB3hL6yD1
OPENWISP_API_TOKEN=8f3c1e0a9b2d4c6e8f0a1b2c3d4e5f60718293a4

The openwisp-firstboot.service one shot is ordered before the application services, so the admin UI only ever starts once every per VM secret has been generated. The values above are an example; your instance will have its own unique credentials.

The openwisp first boot service is enabled and its unit description showing the per VM secret rotation ordered before the app services

Step 4 - Confirm the services are running

Check that PostgreSQL, Redis, the OpenWISP application, the Celery worker, the Celery beat scheduler and nginx are all active:

sudo systemctl is-active postgresql redis-server openwisp openwisp-celery openwisp-celery-beat nginx

All six report active. gunicorn binds to loopback only (127.0.0.1:8001) and nginx publishes the interface on port 80:

sudo ss -tlnp | grep -E ':80 |:8001 '

The six OpenWISP services reporting active and the listening ports, nginx on 80 and gunicorn on 127.0.0.1:8001

Step 5 - Confirm the web endpoints

The admin sign in page returns 200. OpenWISP requires authentication for everything else, so an anonymous request to the application is redirected to the login page and the REST API returns 401 without a token. This is the secure default.

curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://localhost/admin/login/
curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://localhost/
curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://localhost/api/v1/controller/device/

The admin login page returning 200, an anonymous request redirected to login, and the REST API returning 401 without a token

Step 6 - Sign in to the admin UI

Browse to http://<vm-ip>/admin/ and sign in as admin with the password from /root/openwisp-credentials.txt.

The OpenWISP admin sign in page served on port 80

After signing in you land on the OpenWISP dashboard, with charts summarising configuration status, geographic positioning, device models and firmware versions across your fleet. The screenshots in this guide show OpenWISP populated with a small set of example objects; a freshly deployed instance starts empty and ready for you to model your own network.

The OpenWISP admin dashboard after signing in, showing the configuration status and geographic positioning charts

Step 7 - Define a template and add devices

OpenWISP Controller manages device configuration through templates and devices. Under Configurations > Templates you define a reusable configuration (for example a set of network interfaces and a wireless radio) using the NetJSON/OpenWrt backend; under Devices you register each router or access point and apply one or more templates to it. The controller then compiles each device's final configuration from its templates and per device variables.

The OpenWISP Devices list showing example access points with their organization, backend and configuration status

Open a device to see its managed configuration. The device change page has Overview, Configuration, Map and Credentials tabs, a Preview configuration button that shows the compiled output, and a Download configuration button that produces the archive a device fetches when it checks in.

The change page for device ap-branch-01 showing its organization, MAC address, unique key and the Preview and Download configuration controls

Step 8 - See the controller generate a device configuration

The whole point of the controller is that it compiles each device's configuration from its templates. You can render a device's configuration from the command line to see exactly what it will serve. The manage.py command is wrapped as sudo openwisp-manage:

echo "from swapper import load_model
D = load_model('config','Device')
d = D.objects.first()
print(d.config.backend_instance.render()) if d and hasattr(d,'config') else print('no devices yet - add one in the admin UI')" | sudo openwisp-manage shell

On the example instance this renders a full OpenWrt UCI configuration for the access point, including its hostname, its eth0 interface and its wlan0 wireless interface in access point mode:

The controller rendering the compiled OpenWrt UCI configuration for a device, including the system hostname, the eth0 interface and the wlan0 access point radio

Step 9 - Use the REST API

OpenWISP exposes a complete REST API under /api/v1/. Use the token from /root/openwisp-credentials.txt in the Authorization: Bearer <token> header to call any endpoint. Read the token into a shell variable and list your devices and templates:

TOKEN=$(sudo grep '^OPENWISP_API_TOKEN=' /root/openwisp-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'devices  HTTP %{http_code}\n' -H "Authorization: Bearer $TOKEN" http://localhost/api/v1/controller/device/
curl -s -o /dev/null -w 'templates HTTP %{http_code}\n' -H "Authorization: Bearer $TOKEN" http://localhost/api/v1/controller/template/

Both return 200 with your data. Without the token, or with a wrong one, the API returns 401, so the network data is never anonymously readable. The interactive API documentation is available in the admin under the API section.

Step 10 - Add the other OpenWISP modules

This image ships the OpenWISP Controller core (configuration management, PKI, geo, connections, subnet division, users and IP address management). OpenWISP is modular, and you can layer the other modules onto the same instance in the same Python virtual environment:

  • openwisp-monitoring - device metrics, health checks and alerts
  • openwisp-firmware-upgrader - mass firmware upgrades
  • openwisp-radius - a full RADIUS stack for captive portals and 802.1x
  • openwisp-network-topology - live network topology collection and visualisation

Each is installed with sudo /opt/openwisp/venv/bin/pip install <module>, added to INSTALLED_APPS and urls.py in /opt/openwisp/project/openwisp2/, then activated with sudo openwisp-manage migrate and a restart. The monitoring module in particular benefits from a larger VM size. Our support team can help you plan and roll these out.

Step 11 - Enable HTTPS with your own domain

OpenWISP ships on plain HTTP on port 80 so it works immediately. For production, point a DNS record at the VM's public IP, add the hostname to ALLOWED_HOSTS and CSRF_TRUSTED_ORIGINS in /opt/openwisp/project/openwisp2/local_settings.py, and terminate TLS with a certificate. A common approach is to obtain a certificate with certbot and reverse proxy through nginx, or to place Azure Application Gateway in front of the VM. Restart the application after editing the configuration:

$ sudo systemctl restart openwisp openwisp-celery openwisp-celery-beat

Administration and support

The OpenWISP manage.py command is wrapped for convenience: run sudo openwisp-manage <command> (for example sudo openwisp-manage createsuperuser to add another administrator, or sudo openwisp-manage changepassword admin to reset one). The Django settings live in /opt/openwisp/project/openwisp2/, and OpenWISP and its Celery services are managed with sudo systemctl restart openwisp openwisp-celery openwisp-celery-beat.

This image is a repackaged open source software product with additional charges for cloudimg support services. Our engineers provide 24/7 support for deployment, upgrades, TLS termination, adding the OpenWISP monitoring, firmware, RADIUS and topology modules, VPN and PKI setup, API automation and scaling. OpenWISP is a trademark of its respective owner; use of the name here is purely to identify the software this image packages.