Installing Cisco Nexus Dashboard on Proxmox

Important disclaimer
This guide is intended strictly for lab, testing, and learning purposes.

Cisco does not officially support running Nexus Dashboard (ND) on Proxmox.
For production deployments, always use Cisco-supported platforms such as VMware ESXi or bare-metal / supported KVM environments.


VM Creation on Proxmox

Create a new VM in Proxmox with the following characteristics:

proxmox nd vm

One critical requirement is to add a Serial Port to the VM hardware. This is needed because Nexus Dashboard completes part of its installation via SOL (Serial Over LAN).


Monitoring the Installation via Serial Console

When you boot the VM, the graphical console will stop and display a message indicating that installation continues over SOL.

On the Proxmox host, connect to the VM’s serial console:

qm terminal <vmid>

Example:

qm terminal 137

You should see output similar to:

starting serial terminal on interface serial0 (press Ctrl+O to exit)
<lines removed>
Installing for i386-pc platform.
Installation finished. No error reported.
<lines removed>
Reached target Shutdown

=========================================================
Installation completed successfully. Node is powered off
Please unmount vMedia and power on
=========================================================

At this point:

  1. Remove the Nexus Dashboard ISO from the virtual CD-ROM
  2. Boot the VM normally

NIC Naming Issue on Proxmox (Important)

Nexus Dashboard expects specific NIC names in virtualized environments:

  • mgmt0, mgmt1
  • fabric0, fabric1

This works automatically on VMware, but not on Proxmox.
Without fixing this, the node will boot but have no network connectivity.

Workaround: Rename Interfaces Using systemd .link Files


Boot into Rescue Mode

At the MBR menu, press E to edit the boot entry.

Find the line starting with:

linux /vmlinuz...

Make the following changes only:

  • Replace ro with rw
  • Append: systemd.unit=rescue.target

Do not modify anything else.

Boot using Ctrl+X or F10.


Enter Maintenance Mode

When prompted with:

Press Enter for maintenance
(or press Control-D to continue):

Press Enter.

Remount the root filesystem as read/write:

mount -o remount,rw /

Identify Current Interface Names and MAC Addresses

Run:

ip link

Example output:

2: ens18: <BROADCAST,MULTICAST> mtu 1500 state DOWN
    link/ether bc:24:11:c0:52:ff
3: ens19: <BROADCAST,MULTICAST> mtu 1500 state DOWN
    link/ether bc:24:11:bf:96:96
4: ens20: <BROADCAST,MULTICAST> mtu 1500 state DOWN
    link/ether bc:24:11:08:7d:1c
5: ens21: <BROADCAST,MULTICAST> mtu 1500 state DOWN
    link/ether bc:24:11:e8:94:59

Make note of each MAC address — this is crucial.


Create systemd Network Link Files

Create four .link files, one per interface, under:

/etc/systemd/network/

Example filenames:

  • 10-mgmt0.link
  • 10-mgmt1.link
  • 10-fabric0.link
  • 10-fabric1.link

Filenames must:

  • Start with 10-
  • End with .link

Only vi is available, so use it to create the files.

Example: mgmt0

vi /etc/systemd/network/10-mgmt0.link

Contents:

[Match]
MACAddress=bc:24:11:c0:52:ff

[Link]
Name=mgmt0

Repeat this process for:

  • mgmt1
  • fabric0
  • fabric1

?? Be careful to match the correct MAC address for each interface.
Copy/paste errors here are very common.


Reboot the VM

Once all files are created:

reboot

First Boot Nexus Dashboard Setup

Wait for the console message:

Press any key to run first-boot setup on this console...

Press any key and follow the setup wizard.

Example session:

Starting Nexus Dashboard setup utility
Welcome to Nexus Dashboard 4.1.1g

Admin Password:
Reenter Admin Password:

Management Network:
  IP Address/Mask: x.x.x.124/24
  Gateway: x.x.x.1

Is Cluster Leader? (Y/n): y

Important
Make sure you assign an IP address that is reachable from your LAN.

Confirm the configuration and continue.


System Initialization

After a few minutes, you should see:

System initialized successfully
System UI online, please login to https://x.x.x.124

Log in on the console using:

  • Username: rescue-user
  • Password: the one you configured earlier

Final Verification

Verify interface names:

ip link

You should now see:

  • mgmt0
  • mgmt1
  • fabric0
  • fabric1

At this point, networking should be fully functional.


Access Nexus Dashboard

Open a browser and navigate to:

https://<your-ip-address>

Done!
You now have Cisco Nexus Dashboard running on Proxmox for lab and learning purposes.

Happy labbing!

Linux For Network Engineers (LFNE) – AlmaLinux & Alpine Editions

After the release of the Ubuntu 24.04 edition of Linux For Network Engineers (LFNE) I’ve got some questions from the community. Here are two new flavors of LFNE based on your requests.

LFNE AlmaLinux 10 OS

For Red Hat fans who prefer a RHEL-style environment. Since CentOS is no longer maintained, AlmaLinux is the closest drop-in replacement and offers the same look and feel many engineers are used to.

docker pull ipnetxyz/lfne:almalinux-10

LFNE Alpine 3.22 OS

A lightweight edition designed for speed and efficiency. Alpine has a very small footprint, making it ideal for environments where resources are tight or for users who prefer a minimal base to build upon.

docker pull ipnetxyz/lfne:alpine-3.22

Same Tools, Different Base

All editions come with the same curated toolset of networking utilities, Python libraries, and automation tools. The main difference is the base operating system:

EditionBase OSBest ForNotes
Ubuntu 24.04Ubuntu LTSGeneral use, widest compatibilityEasiest to get started with
AlmaLinux 10RHEL-style OSRed Hat fans, enterprise-like environmentsDrop-in CentOS successor
Alpine 3.22Alpine LinuxLightweight setups, minimal footprintVery small and fast

If you’re new to LFNE, check out the Ubuntu 24.04 post for the full list of included tools and usage details.

Linux For Network Engineers (LFNE) – Now on Ubuntu 24.04

The Linux For Network Engineers (LFNE) Docker container has been refreshed and is now built on Ubuntu 24.04 LTS.

For those new to it, LFNE is a ready-to-use Linux environment preloaded with the most popular tools used by network engineers—from packet capture and traffic analysis utilities to configuration helpers and scripting support. Instead of spending time installing and configuring everything manually, LFNE provides a consistent containerized lab environment you can run anywhere.

What’s New in This Release

This upgrade ensures LFNE benefits from the latest long-term support release of Ubuntu, bringing:

  • Updated system libraries and security patches
  • Better compatibility with modern networking tools
  • Some changes for the network tools included

Key Changes

While most workflows remain the same, there are a few important updates under the hood:

  • Python 2 support: Ubuntu 24.04 no longer provides Python 2 packages via apt. To maintain compatibility, Python 2.7.18 is compiled from source inside the container.
  • Python 3 libraries: due to new restrictions, they are installed via pip inside a dedicated Python virtual environment /root/.venv.
    Please activate the virtual environment to use them source /root/.venv/bin/activate
  • New Docker Hub location: To align with the blog name (ipnet.xyz), the images are now published under the username ipnetxyz.

Tools Included

LFNE comes preinstalled with a curated set of tools and libraries that network engineers use most often, including:

  • Core networking tools:
    • OpenSSL
    • Net-tools (ifconfig, etc.)
    • IPutils (ping, arping, traceroute, etc.)
    • Socat
    • Host (DNS lookup tool)
    • MTR (advanced traceroute)
    • Telnet / SSH client
    • IProute2
    • IPerf (traffic generator)
    • TCPDump
    • Nmap
    • OpenSSH Server
  • Automation & Infrastructure-as-Code tools:
    • Ansible
  • Python environments & libraries:
    • Python 2
    • Python 3
    • Paramiko
    • Netmiko
    • Pyntc
    • Napalm

Contribute & Feedback

LFNE is meant to be a handy toolbox for network engineers, but I know I haven’t included every useful tool out there. If there’s something you use all the time and think it belongs in LFNE, let me know! I’m always open to adding tools that make the container more useful for the community.

Getting Started

Pull the latest updated image with:

docker pull ipnetxyz/lfne:ubuntu-24.04

Then start a container interactively with:

docker run -it ipnetxyz/lfne:ubuntu-24.04 /bin/bash

This will drop you into a shell inside LFNE, ready with all the tools you need.

Additional LNFE based on different OS

Additional images have been added (after this blog was posted) based on AlmaLinux OS and Alpine OS. Please find more details here: https://ipnet.xyz/2025/09/linux-for-network-engineers-lfne-almalinux-alpine-editions/

Deprecation Notice

The old images under yotis/lfne:distro-version will remain available but will no longer be updated. Going forward, please switch to the new naming convention:

yotis/lfne:distro-version   ->   ipnetxyz/lfne:distro-version

This ensures you get the latest updates, security patches, and new features.

Testing Arista AVD with GNS3 and EOS


Arista AVD (Architect, Validate, Deploy) – https://avd.arista.com – is a powerful tool that brings network architecture into the world of Infrastructure-as-Code. I wanted to try it out in a lab setting and see how it works in a non-standard environment.

Since my go-to lab tool is GNS3 with Arista cEOS images — while the AVD documentation is primarily built around vEOS — I ran into a few issues. After some troubleshooting, I got it working, and I’d like to share the process here.

This is not a full deployment guide for AVD. Instead, I’ll walk you through the setup I used to make it work in a test environment using GNS3 and cEOS images.


Prerequisites

Make sure your Ansible host has at least 2048MB of memory — I encountered memory-related errors that were otherwise unrelated to the steps below.

Environment Setup

Make sure you’re in your user’s home directory. In my case, the user is debian on the Ansible host.

cd ~
python3 -m venv .avd
source /home/debian/.avd/bin/activate
pip install "pyavd[ansible]"
ansible-galaxy collection install arista.avd

The above will activate a virtual environment for pip use and install the needed packages. The Ansible collections will under .ansible in the home directory.

Copy the AVD example configurations to a work directory (I chose avd)

mkdir avd
cd avd
ansible-playbook arista.avd.install_examples

Make sure you are now be in the ~/avd/ directory to avoid future errors.

(.avd) debian@debian:~/avd$ pwd
/home/debian/avd

You should see the following directories:

ls -la
total 40
drwxr-xr-x 10 debian debian 4096 Jul 24 17:25 .
drwxr-xr-x  7 debian debian 4096 Jul 24 17:23 ..
drwxr-xr-x  7 debian debian 4096 Jul 24 17:25 campus-fabric
drwxr-xr-x  2 debian debian 4096 Jul 24 17:25 common
drwxr-xr-x  8 debian debian 4096 Jul 24 17:24 cv-pathfinder
drwxr-xr-x  7 debian debian 4096 Jul 24 17:24 dual-dc-l3ls
drwxr-xr-x  7 debian debian 4096 Jul 24 17:25 isis-ldp-ipvpn
drwxr-xr-x  7 debian debian 4096 Jul 24 17:23 l2ls-fabric
drwxr-xr-x  7 debian debian 4096 Jul 24 17:24 single-dc-l3ls
drwxr-xr-x  7 debian debian 4096 Jul 24 17:24 single-dc-l3ls-ipv6

Project: single-dc-l3ls

I chose to use the single-dc-l3ls example (https://avd.arista.com/5.5/ansible_collections/arista/avd/examples/single-dc-l3ls/index.html)

My GNS3 topology follows exactly the scenario above in terms of switch number, naming, connections, etc…

gns3 avd ceos 1

Tweak Ansible Config

By default, Ansible only warns when encountering duplicate keys in YAML files. Arista recommends treating this as an error to ensure cleaner configurations.

Update the ansible.cfg in the project folder:

sed -i '/^jinja2_extensions/a\duplicate_dict_key=error' single-dc-l3ls/ansible.cfg

Verify the result:

cat single-dc-l3ls/ansible.cfg

[defaults]
inventory=inventory.yml
jinja2_extensions = jinja2.ext.loopcontrols,jinja2.ext.do,jinja2.ext.i18n
duplicate_dict_key=error

Management Interface Considerations: vEOS vs cEOS

AVD examples assume vEOS images, where the management interface is Management1.

In GNS3 with cEOS, the interface varies. If you followed Arista’s guide for cEOS image installation on GNS3 (https://arista.my.site.com/AristaCommunity/s/article/veos-ceos-gns3-labs) then your management interface is likely Ethernet21.
That’s what I used in my setup.


Initial Configurations for GNS3

Before running the playbook, the management connections must be in place and Ansible host reachable — otherwise, the playbook will not work.

Initial configurations are stored here:

ls -la single-dc-l3ls/switch-basic-configurations/
total 40
drwxr-xr-x 2 debian debian 4096 Jul 24 21:18 .
drwxr-xr-x 7 debian debian 4096 Jul 24 21:24 ..
-rw-rw-r-- 1 debian debian  978 Jul 24 21:18 dc1-leaf1a-basic-configuration.txt
-rw-rw-r-- 1 debian debian  978 Jul 24 21:18 dc1-leaf1b-basic-configuration.txt
-rw-rw-r-- 1 debian debian  978 Jul 24 21:18 dc1-leaf1c-basic-configuration.txt
-rw-rw-r-- 1 debian debian  978 Jul 24 21:18 dc1-leaf2a-basic-configuration.txt
-rw-rw-r-- 1 debian debian  978 Jul 24 21:18 dc1-leaf2b-basic-configuration.txt
-rw-rw-r-- 1 debian debian  978 Jul 24 21:18 dc1-leaf2c-basic-configuration.txt
-rw-rw-r-- 1 debian debian  977 Jul 24 21:18 dc1-spine1-basic-configuration.txt
-rw-rw-r-- 1 debian debian  977 Jul 24 21:18 dc1-spine2-basic-configuration.txt

Update the management interface to Ethernet21:

sed -i 's/Management1/Ethernet21/g' single-dc-l3ls/switch-basic-configurations/*.txt

Copy the configs to each switch in the GNS3 lab and make sure they can ping the Ansible host.


Modify inventory.yml

Update the Ansible host IP to match your topology. In my setup, it’s in the 172.16.1.0/24 range:

nano /home/debian/avd/single-dc-l3ls/inventory.yml

Change:

ansible_host: 192.168.1.12

To:

ansible_host: 172.16.1.254

Everything else can remain unchanged if you’re following the same example from Arista’s site.


Update Playbook for Management Interface

The intended/configs/ files also reference Management1. These need to be changed to Ethernet21.

ls -la single-dc-l3ls/intended/configs/
total 80
drwxr-xr-x 2 debian debian 4096 Jul 24 21:18 .
drwxr-xr-x 4 debian debian 4096 Jul 24 21:18 ..
-rw-rw-r-- 1 debian debian 9098 Jul 24 21:18 dc1-leaf1a.cfg
-rw-rw-r-- 1 debian debian 9098 Jul 24 21:18 dc1-leaf1b.cfg
-rw-rw-r-- 1 debian debian 1942 Jul 24 21:18 dc1-leaf1c.cfg
-rw-rw-r-- 1 debian debian 9106 Jul 24 21:18 dc1-leaf2a.cfg
-rw-rw-r-- 1 debian debian 9110 Jul 24 21:18 dc1-leaf2b.cfg
-rw-rw-r-- 1 debian debian 1942 Jul 24 21:18 dc1-leaf2c.cfg
-rw-rw-r-- 1 debian debian 4187 Jul 24 21:18 dc1-spine1.cfg
-rw-rw-r-- 1 debian debian 4191 Jul 24 21:18 dc1-spine2.cfg

Update the management interface:

sed -i 's/Management1/Ethernet21/g' single-dc-l3ls/intended/configs/*.cfg

Also, make Ethernet21 a Layer 3 port:

sed -i '/^interface Ethernet21$/a\   no switchport' single-dc-l3ls/intended/configs/*.cfg

Run the Playbook

Now you’re ready to deploy.

cd single-dc-l3ls
ansible-playbook deploy.yml

Expected output:

PLAY RECAP *********************************************************************
dc1-leaf1a                 : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
dc1-leaf1b                 : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
dc1-leaf1c                 : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
dc1-leaf2a                 : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
dc1-leaf2b                 : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
dc1-leaf2c                 : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
dc1-spine1                 : ok=4    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
dc1-spine2                 : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Make sure unreachable, failed, and skipped are all 0 — that’s your confirmation that everything went smoothly.


Summary

While AVD examples are designed around vEOS, it’s perfectly possible to adapt it for cEOS in GNS3 with a few changes. The most important steps involve:

  • Updating interface names
  • Ensuring management connectivity stays up
  • Modifying inventory and config files accordingly

This lab-friendly workflow lets you explore AVD’s potential without dedicated hardware or CVP.

Deploying ELK Stack with Docker Compose (2025 Edition)

This guide walks you through installing and configuring the ELK Stack (Elasticsearch, Logstash, Kibana, and Filebeat) using Docker Compose. It is fully updated for Elasticsearch 9.0.2 and explains the necessary changes for versions 8+ and above, including the required security setup and user permissions.

Prerequisites

Ensure you have the following installed on your system:

  • Docker: Install Docker Engine or Docker Desktop.
  • Docker Compose: Typically included with Docker Desktop; otherwise, install it separately

The folder structure for is as follows:

elk-lab/
|-- .env
|-- docker-compose.yml
|-- logstash/
|   |-- pipeline/
|       |-- logstash.conf
|-- filebeat
|   |-- filebeat.yml

Start by creating your root folder structure

mkdir elk-docker && cd elk-docker
mkdir -p logstash/pipeline
mkdir filebeat

Step 1: Prepare Your .env File

Create a .env file in the root of your project directory:

ELASTIC_USER=myadmin
ELASTIC_PASSWORD=mypassw0rd!

For simplicity in this lab setup, all components share the same password. Do not use this setup in production.

Step 2: docker-compose.yml

Create a docker-compose.yml file:

services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:9.0.2
    container_name: elasticsearch
    environment:
      - node.name=elasticsearch
      - discovery.type=single-node
      - xpack.security.enabled=true
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
    ports:
      - "9200:9200"
    volumes:
      - esdata:/usr/share/elasticsearch/data
    networks:
      - elk

  kibana:
    image: docker.elastic.co/kibana/kibana:9.0.2
    container_name: kibana
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
      - ELASTICSEARCH_USERNAME=${ELASTIC_USER}
      - ELASTICSEARCH_PASSWORD=${ELASTIC_PASSWORD}
    ports:
      - "5601:5601"
    depends_on:
      - elasticsearch
    networks:
      - elk

  logstash:
    image: docker.elastic.co/logstash/logstash:9.0.2
    container_name: logstash
    environment:
      - xpack.monitoring.enabled=true
      - ELASTIC_USERNAME=${ELASTIC_USER}
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
    ports:
      - "5044:5044"
    volumes:
      - ./logstash/pipeline:/usr/share/logstash/pipeline
    depends_on:
      - elasticsearch
    networks:
      - elk

  filebeat:
    image: docker.elastic.co/beats/filebeat:9.0.2
    container_name: filebeat
    user: root
    environment:
      - ELASTICSEARCH_USERNAME=${ELASTIC_USER}
      - ELASTICSEARCH_PASSWORD=${ELASTIC_PASSWORD}
    volumes:
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./filebeat/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
    depends_on:
      - logstash
    networks:
      - elk
    command: ["--strict.perms=false"]

volumes:
  esdata:

networks:
  elk:

Step 3: Logstash Pipeline

Create the Logstash pipeline file at ./logstash/pipeline/logstash.conf:

input {
  beats {
    port => 5044
  }
}

output {
  elasticsearch {
    hosts => ["http://elasticsearch:9200"]
    user => "myadmin"
    password => "mypassw0rd!"
    index => "logstash-%{+YYYY.MM.dd}"
  }
}

Step 4: Filebeat Config

Create the Filebeat config at ./filebeat/filebeat.yml with this content:

filebeat.inputs:
  - type: filestream
    id: my-container-logs
    enabled: true
    paths:
      - /var/lib/docker/containers/*/*.log
    parser:
      - docker

output.logstash:
  hosts: ["logstash:5044"]
  username: "${ELASTIC_USER}"
  password: "${FILEBEAT_PASSWORD}"

setup.kibana:
  host: "kibana:5601"
  username: "${ELASTIC_USER}"
  password: "${FILEBEAT_PASSWORD}"

# Optional for debugging
logging.level: info
logging.to_files: false

Just a note: In this setup the Filebeat output to Logstash (not Elasticsearch) because in general I use ELK with netflow so I can work with more complex logs parsing.

If you chose to send Filebeat to Elasticsearch just adapt the fiebeat.yml file becasue the docker-compose.yml is already ready.

Step 5: Start Elasticsearch Container Before Creating Users and Roles (Temporary Step)

Before creating the custom user and roles, you must start at least the Elasticsearch container once to enable the security APIs.

Run this command in your project directory:

docker compose up -d elasticsearch

If this is the first time starting the container, Docker will first download the elasticsearch image.

Wait a minute for Elasticsearch to fully start and be ready to accept API calls. You can check logs with:

docker logs -f elasticsearch

Only after Elasticsearch is up and running, proceed with creating roles and users in the next step.

Step 6: Create the Custom User and Roles

As of version 8+, Kibana no longer allows login with the elastic user for Fleet and integrations. Instead, you must create a new superuser with additional privileges.

  1. Create a new role that allows access to restricted indices:
curl -X POST http://localhost:9200/_security/role/kibana_system_access \
  -u elastic:mypassw0rd! \
  -H "Content-Type: application/json" \
  -d '{
    "cluster": ["all"],
    "indices": [
      {
        "names": [".kibana*", ".apm*"],
        "privileges": ["all"],
        "allow_restricted_indices": true
      }
    ]
  }'

2. Create a new superuser myadmin:

curl -X POST http://localhost:9200/_security/user/myadmin \
  -u elastic:mypassw0rd! \
  -H "Content-Type: application/json" \
  -d '{
    "password": "mypassw0rd!",
    "roles": ["superuser", "kibana_system_access"],
    "full_name": "Lab Admin"
  }'

After both commands usually there is an output which contains {"created":true} among other details. This is an indication that the role and user were created successfully.

Step 7: Launch the Full Stack

Now that the users and roles are created, start all services:

docker compose up -d

Access Kibana at:

http://localhost:5601

For lab topics, use the IP address of your Docker host:

http://docker_host_ip:5601

Use credentials:

  • Username: myadmin
  • Password: mypasw00rd!

Notes

  • Why not use the elastic user? Since v8+, the elastic user is intended for initial setup and API use only — not for logging into Kibana.
  • Why grant access to restricted indices? Kibana uses internal system indices like .kibana_* and .apm_* that are restricted by default. Your user must explicitly have permissions to manage these indices.
  • Why is security mandatory? Features such as Fleet, Integrations, and Kibana dashboards require security to be enabled, meaning all services must use authenticated users.