Building a Modern, Web-Based Serial Console Server
In the world of network engineering, the “out-of-band” (OOB) management link is our last line of defence. When a remote router loses its routing table or a core switch panics, SSH is the first thing to go. Historically, this meant a physical trip to the data centre or comms room with a laptop and a turquoise Cisco console cable. This is may lead to Larger downtime and time take to travel to far places.
Today, we can build a high-availability Web-Based Serial Console Server using open-source tools like ser2net, FastAPI, Nginx. This post explores how to modernise serial access.
You have plenty of cost effective USB to RS232 Serial cable available in the market, and any OLD PC with USB ports can be used for Console Server.
I have built this console server for less than £200 (I used an old Cisco Router with Serial cables, which I feel is an old-fashioned way of working).
Hardware :
- One OLD HP Portable Desk PC (which has 2 USB ports and 1 Ethernet)
- 2 Qty 4 Port USB to RS232 Cable
Software :
- Ubuntu 24.x LTS
- Ser2net
- ttyd
- socat
- nginx – reverse proxy
- FastAPI (Python Virtual Environment) run Locally.
Installation :
- Ubuntu, you can download from the Ubuntu site, install Minimal
- With Static IP (this IP and the connected Ethernet port – where OOB management network) – that is beneficial when the Core network down, you can reach console access via OOB.
- Ser2Net – acts as the proxy between the physical serial port and a network socket. In a modern YAML configuration, we define our endpoints to allow multiple connection.
- ttyd is a command-line tool that shares a terminal over the web.
- socat (Short for “Socket Cat”) is a heavy-duty utility that connects two different data streams together.
- The Frontend: Web-Based Terminal Access The “magic” happens when we bring this into the browser. Using a Python-based backend (FastAPI), we can serve a dashboard that manages user sessions and encapsulates terminal traffic.
- nginx as a reverse proxy to connect to Local Fast API server to protect the logic behind.
#sudo apt update && sudo apt install python3-pip python3-venv nginx ser2net socat ttyd -y
Folder for application
#mkdir terminal-backend
#cd terminal-backend
activate python environment
#python3 -m venv venv
#pip install fastapi uvicorn[standard] sqlalchemy python-multipart
create a folder structure
.
├── main.py
|__ index.html
├── add_user.py
├── update_pass.py
├── user.db
└── assets/
├── fonts/
│ ├── local-fonts.css
│ ├── inter-v12-latin-regular.woff2
│ └── jetbrains-mono-v13-latin-regular.woff2
└── icons/
└── bootstrap-icons.css
main.py content in short

ser2net.yaml
connection: &con01
accepter: tcp,2001
connector: serialdev,/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:2:1.0-port0,9600n81,local
options:
kickolduser: true
trace-read: /var/log/ser2net/switch1.log
nginx site-enable – default
server {
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl default_server;
server_name _;
# Point to your newly generated SSL files
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
# Standard security protocols
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://127.0.0.1:8000; # Points to FastAPI
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
CONSOLES
# ----------------------------
location /console1 {
proxy_pass http://127.0.0.1:3001;
proxy_buffering off;
proxy_request_buffering off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection upgrade;
proxy_set_header Host $host;
I will map each console as service to run port redirection
console1.service
[Unit]
Description=TTYd Console 1
After=network.target ser2net.service
[Service]
ExecStart=/usr/bin/ttyd -p 3001 -b /console1 -W /usr/bin/script -q -a -f -c "/usr/bin/socat -,raw,echo=0 tcp:127.0.0.1:2001" /var/log/ttyd/console1.log
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
simple HTML index.html
<!DOCTYPE html>
<html>
<head>
<title>Console View</title>
<style>
body { margin: 0; background: #000; color: #00e676; font-family: monospace; }
.header { padding: 10px; background: #1a1d29; border-bottom: 1px solid #2d3142; }
iframe { width: 100%; height: calc(100vh - 50px); border: none; background: #000; }
</style>
</head>
<body>
<div class="header">SYSTEM CONSOLE: CORE-SWITCH-01</div>
<!-- Point the src to your ttyd instance -->
<iframe src="http://10.10.10.10:8080" allow="clipboard-read; clipboard-write"></iframe>
</body>
</html>

Web Console

Happy Labbinggggggggggggg !