Deployment guide

GNS3 Server

Videos

Overview: GNS3 Server on Ubuntu 24.04 LTS on Azure: overview (watch on YouTube)

Launch and log in: GNS3 Server on Ubuntu 24.04 LTS on Azure: launch and log in (watch on YouTube)

Deep Dive: GNS3 Server on Ubuntu 24.04 LTS on Azure: deep dive (watch on YouTube)

Overview

GNS3 is the widely used open-source network emulation platform for designing, building and testing network topologies without physical hardware. It runs virtual labs made of emulated Cisco routers, Docker containers, QEMU virtual machines and lightweight virtual PCs. You wire them together into topologies that you can start, stop and inspect.

This SecureAnyCloud image ships the server component: the headless controller and compute engine that actually runs the labs. You drive it from the bundled browser web interface, or by pointing the GNS3 desktop client at it. Running the server on Azure rather than a laptop means that labs keep running when you close your machine, they get server-grade CPU and memory, and a whole team can share them.

This guide covers the deployment and configuration of GNS3 Server on Ubuntu 24.04 on Azure using SecureAnyCloud Azure Marketplace images.

The image installs GNS3 Server 2.2.60 and the complete emulation stack at pinned versions from the official GNS3 package archive. The archive's signing key is verified by full fingerprint before the archive is trusted. The baked versions are recorded in /var/lib/gns3/VERSION.

GNS3 runs under systemd as the unprivileged gns3 system user, bound to loopback only (127.0.0.1:3080). nginx is the sole network-facing surface. It terminates TLS on port 443, enforces HTTP basic authentication on every request and reverse proxies to GNS3. Port 80 redirects to HTTPS.

Secure by default, which matters more here than for most software. An unprotected GNS3 interface lets anyone who can reach it create and start containers and virtual machines on the host. An open interface is therefore equivalent to handing out shell access.

This image closes the interface at two independent layers using a single credential. GNS3's own HTTP authentication is enabled on the loopback listener, and nginx enforces HTTP basic authentication on every path in front of it. A unique credential is generated on each virtual machine's first boot and applied to both layers.

Only a one-way bcrypt hash is stored for nginx. The plain password is placed in a root-only file. There are no shared or default credentials, and nothing usable is baked into the image. Device consoles are bound to the loopback interface rather than the network, and you reach them over an SSH tunnel (Step 8).

What you get:

  • GNS3 Server 2.2.60 run under systemd as the unprivileged gns3 system user, with projects, images and configs under /var/lib/gns3
  • The complete emulation stack at pinned versions: Dynamips 0.2.23 (Cisco IOS router emulation), ubridge 1.0.1 (node networking), VPCS 0.8.3 (lightweight virtual PCs), QEMU 8.2.2 (full virtual machines) and Docker (container nodes)
  • A ready-to-run Docker appliance template (alpine:3.24.1, pinned by digest), so you can start a real node immediately without downloading anything
  • GNS3 bound to loopback only (127.0.0.1:3080) with its own authentication enabled, so it is never directly exposed to the network
  • nginx terminating TLS on :443, enforcing HTTP basic authentication and reverse proxying to GNS3, with HSTS and sensible security headers
  • Port :80 returns a 301 redirect to HTTPS for every path except an unauthenticated /healthz endpoint (HTTP 200) for load balancer probes
  • A unique API credential generated per VM on first boot and applied to both authentication layers. Only the one-way bcrypt hash is stored for nginx, and the plain password lands in a root-only file
  • Device consoles pinned to loopback (console_host = 127.0.0.1), because telnet consoles are unauthenticated by protocol
  • A self-signed TLS certificate regenerated per VM on first boot. Its Subject Alternative Names include the VM public IP, hostname and 127.0.0.1. Replace it with your own CA-signed certificate for production
  • A built-in self test at /usr/local/bin/gns3-selftest that proves the authenticated API end to end and starts a real emulated node
  • Ubuntu 24.04 LTS base with the latest security patches applied at build time
  • Azure Linux Agent for seamless cloud integration and SSH key injection
  • 24/7 SecureAnyCloud support with a guaranteed 24 hour response SLA

Prerequisites

  • An Azure subscription with permission to deploy virtual machines
  • An SSH key pair for the azureuser account
  • A network security group that allows inbound 443 from your users and 22 from your management network only
  • Choose the right VM size for the emulation you need. Read Step 1 before you deploy. Dynamips, Docker and VPCS nodes run on any size, while QEMU virtual machine nodes need a size that supports nested virtualisation.
1

Choose a VM size for the emulation you need

GNS3 emulates devices in several ways, and they do not all have the same hardware requirement. This is the single most important sizing decision for this image, so make it before you deploy.

These are the node types and whether each needs nested virtualisation:

  • Dynamips: classic Cisco IOS routers. No nested virtualisation needed, so it runs on any VM size.
  • Docker: container-based appliances and hosts. No nested virtualisation needed, so it runs on any VM size.
  • VPCS: lightweight virtual PCs for testing reachability. No nested virtualisation needed, so it runs on any VM size.
  • Ethernet switch, hub and cloud: built-in switching and connectivity. No nested virtualisation needed, so it runs on any VM size.
  • QEMU and KVM: full virtual machines (vendor appliance images, firewalls, full OS nodes). Nested virtualisation is needed, because these nodes require /dev/kvm.

QEMU nodes are hardware-accelerated through KVM, which on Azure requires nested virtualisation. Azure's B series does not support nested virtualisation, so /dev/kvm is absent there and QEMU-based appliance nodes cannot be accelerated. The Dv3, Dsv3, Dv5 and Dsv5 series (and later) do support it.

If you will use Dynamips, Docker or VPCS nodes only, any size works. Standard_B2s is enough to get started.

If you will use QEMU virtual machine nodes, deploy on Standard_D2s_v3 or larger (any v3 or later D series). This is the recommended size for this image, and it is what the Marketplace listing recommends.

Emulation is CPU- and memory-hungry in general. Size for the lab you intend to run, not for the smallest node count.

2

Deploy from the Azure Portal

Locate the GNS3 Server on Ubuntu 24.04 LTS image in the Azure Marketplace and select Create.

Choose your subscription, resource group and region.

Select a VM size using Step 1 (Standard_D2s_v3 or larger if you need QEMU nodes). Provide your SSH public key for the azureuser account.

On the Networking tab, allow inbound 443 from your users, and restrict 22 to your management network.

Select Review + create, then Create. When the VM is running, browse to https://<your-public-ip>/.

3

Deploy from the Azure CLI

Deploy the image from the command line, restricting SSH to your management network. Replace the example values with your own before you run the command:

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:gns3-server-ubuntu-24-04:default:latest
az vm create \
  --resource-group my-resource-group \
  --name gns3-server \
  --image sac1647283583153:gns3-server-ubuntu-24-04:default:latest \
  --size Standard_D2s_v3 \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_rsa.pub \
  --public-ip-sku Standard
# Allow the web UI from your users and SSH from your management network only
az vm open-port --resource-group my-resource-group --name gns3-server --port 443 --priority 1001
az network nsg rule create --resource-group my-resource-group --nsg-name gns3-serverNSG \
  --name allow-ssh --priority 1002 --destination-port-ranges 22 \
  --source-address-prefixes <your-mgmt-cidr> --access Allow --protocol Tcp

When the VM is running, browse to https://<public-ip>/.

4

Retrieve your per-instance API credential

Access is protected by HTTP basic authentication at the proxy and by GNS3's own authentication behind it. Each VM generates its own username and password on first boot and writes them to a root-only file. Read the file over SSH:

ssh azureuser@<vm-ip> 'sudo cat /root/gns3-server-credentials.txt'

The file contains the GNS3_USER, GNS3_PASS and GNS3_URL for this specific VM. Keep it secret, because this credential can start containers and virtual machines on the server. nginx only ever stores a one-way bcrypt hash of the password.

Issue the certificate for your public address

First boot puts the address that Azure reports into the TLS certificate, the credentials file and the login banner. With a Standard SKU public IP, the current image records the VM's private address (10.0.0.x). Standard is the Azure default and the --public-ip-sku Standard option in Step 3.

As a result, GNS3_URL points at an address you cannot reach, and the certificate does not name the public IP. Check:

sudo grep '^GNS3_URL=' /root/gns3-server-credentials.txt

If it shows a 10. address, issue a new certificate for the public IP on the VM. Azure's instance metadata service reports the public IP to the VM itself, and nothing leaves the VM:

PUBLIC_IP=$(curl -s -H Metadata:true 'http://169.254.169.254/metadata/loadbalancer?api-version=2020-10-01' | grep -o '"frontendIpAddress":"[^"]*"' | head -1 | cut -d'"' -f4)
PRIV=$(hostname -I | awk '{print $1}'); echo "public $PUBLIC_IP, private $PRIV"
sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
  -keyout /etc/nginx/tls/gns3.key -out /etc/nginx/tls/gns3.crt \
  -subj "/C=GB/O=sac/CN=$PUBLIC_IP" \
  -addext "subjectAltName=IP:127.0.0.1,DNS:localhost,DNS:$(hostname -s),IP:$PRIV,IP:$PUBLIC_IP" sudo chmod 0600 /etc/nginx/tls/gns3.key
sudo sed -i "s/\b$PRIV\b/$PUBLIC_IP/g" /root/gns3-server-credentials.txt /etc/update-motd.d/90-gns3-server
sudo systemctl restart nginx

For a DNS name, set PUBLIC_IP to the name and write DNS:$PUBLIC_IP in place of IP:$PUBLIC_IP. Your credential does not change. The certificate is still self-signed, so the browser warning in Step 5 remains until you install a CA-signed certificate (Step 12).

5

Sign in and register the server in the web interface

Browse to https://<vm-ip>/. The certificate is self-signed per VM, so your browser shows a certificate warning the first time. Accept it to proceed, or place a CA-signed certificate in front (Step 12). Your browser then prompts for the basic authentication username and password from Step 4.

The GNS3 Web UI opens on its Servers page. The web interface is a client, so you tell it which GNS3 server to talk to. Click Add server and fill in the connection details for this VM. Choose Basic authorization and enter the credential from Step 4.

Once added, the server appears in the Servers table with its host, port and location, and an arrow action to open it.

6

Create a project

Open the server to reach its Projects page. From here, you can create a blank project, import an existing one, or open a project you already have. Projects are stored on the VM under /var/lib/gns3/projects.

Click a project to open its topology canvas.

7

Build and start a topology

Open the project to reach the topology canvas. Add nodes from the toolbar and drag links between their ports. Use the toolbar controls to start, pause and stop the lab. Started nodes show a green marker, and the node panel lists each node's console address.

The example below is a three-node lab: two Docker-based hosts wired to an Ethernet switch, all started. Each console is a telnet://127.0.0.1:<port> address, because consoles are deliberately bound to the loopback interface. Step 8 covers how to reach them.

You can drive the same operations from the REST API. This example creates a project, adds a Docker node from the bundled alpine:3.24.1 template, starts it, then removes the project again:

GUSER=$(sudo grep -m1 '^GNS3_USER=' /root/gns3-server-credentials.txt | cut -d= -f2-)
GPASS=$(sudo grep -m1 '^GNS3_PASS=' /root/gns3-server-credentials.txt | cut -d= -f2-)
PID=$(curl -sk -u "$GUSER:$GPASS" -X POST -H 'Content-Type: application/json' \
  --data '{"name":"api-demo"}' https://127.0.0.1/v2/projects \
  | sed -n 's/.*"project_id": *"\([^"]*\)".*/\1/p')
curl -sk -u "$GUSER:$GPASS" -X POST -H 'Content-Type: application/json' \
  --data '{"name":"host1","node_type":"docker","compute_id":"local","console_type":"telnet", "properties":{"image":"alpine:3.24.1","adapters":1,"start_command":"/bin/sh"}}' \
  -o /dev/null -w 'create node -> HTTP %{http_code}\n' https://127.0.0.1/v2/projects/$PID/nodes
curl -sk -u "$GUSER:$GPASS" -X POST -o /dev/null -w 'start nodes -> HTTP %{http_code}\n' \
  https://127.0.0.1/v2/projects/$PID/nodes/start
curl -sk -u "$GUSER:$GPASS" -X DELETE -o /dev/null -w 'cleanup -> HTTP %{http_code}\n' \
  https://127.0.0.1/v2/projects/$PID

The console_type field is required when you create a node. GNS3 does not default it.

8

Reach a device console

Device consoles are telnet, which is unauthenticated by protocol. This image therefore binds them to the loopback interface (console_host = 127.0.0.1), so they are never reachable from the network.

To reach a console, forward its port over SSH from your workstation, then connect to the forwarded port locally:

# On your workstation. The console port is shown in the node panel, e.g. 5000.
ssh -L 5000:127.0.0.1:5000 azureuser@<vm-ip> telnet 127.0.0.1 5000

Do not change console_host to 0.0.0.0. That would expose every device console to the network without authentication.

9

Verify the deployment

SSH to the VM as azureuser. Confirm that the services are running and that GNS3 is bound to loopback only:

systemctl is-active gns3-server nginx docker
sudo ss -tlnp | grep -E ':(80|443|3080)\b'

All three services report active, and GNS3 listens on 127.0.0.1:3080 only. nginx is the only component exposed on :80 and :443.

Run the built-in self test. It reads this VM's credential and proves the whole path:

  • The health probe answers.
  • An unauthenticated API request is refused.
  • An authenticated request succeeds.
  • A project is created through the API.
  • A real Docker-backed node is started.

The self test also reports the hardware virtualisation state of this VM:

sudo /usr/local/bin/gns3-selftest
10

The authentication gate and the API

nginx terminates TLS on port 443 and redirects plain HTTP on port 80 to HTTPS. Confirm the redirect and the unauthenticated health probe. Also confirm that the API is refused without the per-VM credential and succeeds with it:

curl -s -o /dev/null -w 'port 80 -> HTTP %{http_code}\n' http://127.0.0.1/
curl -sk -o /dev/null -w 'healthz -> HTTP %{http_code}\n' https://127.0.0.1/healthz
curl -sk -o /dev/null -w 'API, no creds -> HTTP %{http_code}\n' https://127.0.0.1/v2/version
curl -sk -o /dev/null -w 'API, direct to loopback, no creds -> HTTP %{http_code}\n' http://127.0.0.1:3080/v2/version

Port 80 returns 301 and the health endpoint returns 200. The API returns 401 without credentials, both through the proxy and when addressed directly on the loopback listener. This proves that GNS3's own authentication layer is enforcing and is not merely shadowed by nginx.

With the credential, the API answers and reports the node types this server can emulate:

GUSER=$(sudo grep -m1 '^GNS3_USER=' /root/gns3-server-credentials.txt | cut -d= -f2-)
GPASS=$(sudo grep -m1 '^GNS3_PASS=' /root/gns3-server-credentials.txt | cut -d= -f2-)
curl -sk -u "$GUSER:$GPASS" https://127.0.0.1/v2/version
curl -sk -u "$GUSER:$GPASS" https://127.0.0.1/v2/computes | jq -r '.[0].capabilities.node_types | join(", ")'
11

Connect the GNS3 desktop client

The desktop client is the richer way to build large topologies, and it talks to this same server. In the client, choose to connect to a remote server. Supply the VM's address on port 443 with HTTPS, plus the username and password from Step 4.

The certificate is self-signed, so either accept it or install a CA-signed certificate first (Step 12).

Use a matching client version: a GNS3 2.2.x client for this 2.2.60 server.

12

Replace the certificate for production

The per-VM certificate is self-signed, so browsers and the desktop client warn on first use. For a real deployment, put a trusted certificate in front. The simplest approach is to point a DNS name at the VM and obtain a free certificate with Certbot:

# Point https://<your-domain> at this VM first, then:
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d <your-domain>

Alternatively, terminate TLS at an Azure Application Gateway or a load balancer in front of the VM and forward to port 443.

13

Baked versions and emulator inventory

The baked GNS3 and emulator versions are recorded on the VM, along with the pinned Docker appliance template and the archive signing key that was verified at build time:

cat /var/lib/gns3/VERSION dynamips --version | head -1 vpcs -v | head -1 ubridge -v qemu-system-x86_64 --version | head -1

The last command in the screenshot below checks for /dev/kvm. On a B series VM it is absent. This is expected and only affects QEMU nodes, while Dynamips, Docker and VPCS nodes are unaffected. On a Standard_D2s_v3 or larger, it is present and QEMU nodes are hardware-accelerated.

14

Service management

systemctl status gns3-server --no-pager | head -12 sudo journalctl -u gns3-server -n 20 --no-pager

The server log is also written to /var/log/gns3/gns3-server.log. Projects, images, configs and appliances live under /var/lib/gns3. Back up that directory to preserve your labs.

Security notes

No default login. Access is protected at two layers with a single credential generated on each VM's first boot: nginx HTTP basic authentication, and GNS3's own authentication on the loopback listener. Only a one-way bcrypt hash is stored for nginx. The plain password lives in the root-only /root/gns3-server-credentials.txt.

Treat the credential as shell access. The GNS3 API can create and start containers and virtual machines on the server. Anyone with this credential can run code on the VM. Share it accordingly. If you need per-user access, put an identity-aware proxy in front.

Loopback-only application. GNS3 binds to 127.0.0.1:3080 and is reachable only through the nginx TLS reverse proxy on :443. Keep it that way, and let nginx (or a load balancer) be the sole network-facing surface.

Consoles stay on loopback. Device telnet consoles are unauthenticated by protocol, so console_host is pinned to 127.0.0.1. Reach them over an SSH tunnel (Step 8) instead of changing that setting.

Restrict access. Allow 443 only from the networks that need it, and keep 22 restricted to your management network. Replace the self-signed certificate with a CA-signed one for production.

Rotate the credential with sudo /usr/local/sbin/gns3-write-credentials <new-user> <new-password>, then restart the services. This updates both authentication layers together.

Version pinning. The GNS3 packages are installed at exact validated versions from the official GNS3 archive, whose signing key is verified by fingerprint at build time. They are deliberately not placed on apt hold. Ubuntu's unattended upgrades apply only the security origin, so a third-party archive is never upgraded automatically. Holding packages would misrepresent the patch posture of the machine.

OS security updates continue to apply as normal. You remain free to upgrade GNS3 yourself when you choose to.

Support

This image is backed by 24/7 SecureAnyCloud support with a guaranteed 24 hour response SLA. Contact sales@secureanycloud.com for assistance.

GNS3 is open-source software distributed under the GNU General Public License v3.0 and is free. The SecureAnyCloud charge covers packaging, hardening, security patching, image maintenance and support.

Deploy on Azure

Launch GNS3 Server on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure