Intermediate
    30–45 min
    512 MB+ RAM

    Deploy Beszel on a RamNode VPS

    Lightweight hub-and-agent server monitoring backed by PocketBase. CPU, memory, disk, network, and Docker container metrics — all under 50 MB of RAM.

    Part of a series: This standalone guide covers Beszel deployment end-to-end. For a broader look at monitoring tools, see the VPS Monitoring & Observability Series.

    What Is Beszel?

    Beszel is a lightweight, open-source server monitoring platform built around a hub-and-agent architecture. The hub is a web application (backed by PocketBase) where you view dashboards, configure alerts, and manage users. Agents are small Go binaries that run on each server you want to monitor and relay metrics back to the hub.

    Compared to heavier stacks like Prometheus and Grafana, Beszel is considerably lighter on resources. Even running the hub alongside five or more agents, CPU usage typically stays under 2% on a modest VPS.

    CPU, memory, disk, and network metrics with historical graphs
    Per-container Docker stats without extra configuration
    Configurable threshold alerts via email or webhook
    Multi-user support with admin and read-only roles
    S3-compatible backup for hub data
    OAuth2/OIDC support alongside local accounts

    Prerequisites

    • A RamNode KVM VPS running Ubuntu 22.04 or 24.04
    • A non-root sudo user (initial server setup guide)
    • Docker Engine and Docker Compose installed (Docker installation guide)
    • UFW enabled and configured
    • A domain or subdomain pointing to your VPS IP (e.g., monitor.yourdomain.com)
    • nginx and Certbot installed (sudo apt install nginx certbot python3-certbot-nginx -y)
    1

    Open the Required Firewall Ports

    Beszel uses port 8090 for the hub web interface and 45876 for agent-to-hub communication. You'll put nginx in front of 8090, so only expose 80 and 443 publicly.

    Allow HTTP and HTTPS
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp

    If monitoring only the local VPS, you do not need to open port 45876 — the hub and agent communicate over a Unix socket. For remote servers, allow the agent port only from trusted IPs:

    Allow agent port from a trusted IP
    sudo ufw allow from YOUR_REMOTE_SERVER_IP to any port 45876 proto tcp
    sudo ufw reload
    sudo ufw status

    Important: Docker bypasses UFW by writing directly to iptables. To prevent Docker from exposing port 8090 publicly, bind it to localhost only in the Compose file (shown in Step 2).

    2

    Create the Beszel Directory and Compose File

    Create the project directory
    sudo mkdir -p /opt/beszel
    cd /opt/beszel

    Create the Docker Compose file. This deploys the hub and a local agent together, with the hub bound to localhost so nginx is the sole public entry point.

    /opt/beszel/docker-compose.yml
    services:
      beszel:
        image: henrygd/beszel:latest
        container_name: beszel
        restart: unless-stopped
        ports:
          - "127.0.0.1:8090:8090"
        volumes:
          - ./beszel_data:/beszel_data
          - ./beszel_socket:/beszel_socket
        environment:
          APP_URL: "https://monitor.yourdomain.com"
    
      beszel-agent:
        image: henrygd/beszel-agent:latest
        container_name: beszel-agent
        restart: unless-stopped
        network_mode: host
        volumes:
          - ./beszel_agent_data:/var/lib/beszel-agent
          - ./beszel_socket:/beszel_socket
          - /var/run/docker.sock:/var/run/docker.sock:ro
        environment:
          LISTEN: /beszel_socket/beszel.sock
          HUB_URL: "http://localhost:8090"
          TOKEN: "REPLACE_WITH_TOKEN"
          KEY: "REPLACE_WITH_KEY"

    Replace monitor.yourdomain.com with your actual subdomain. Leave TOKEN and KEY as placeholders — you'll fill them in after first login in Step 5.

    3

    Start the Hub Container

    Start only the hub first, leaving the agent offline until you have credentials:

    Start the hub
    cd /opt/beszel
    sudo docker compose up -d beszel
    Verify it's running
    sudo docker ps
    sudo docker logs beszel

    You should see the PocketBase server start on port 8090 with no errors. The agent will fail at this point because the token and key are not set yet — that's expected.

    4

    Configure nginx and Obtain SSL

    Create the nginx server block
    sudo nano /etc/nginx/sites-available/beszel
    /etc/nginx/sites-available/beszel
    server {
        listen 80;
        server_name monitor.yourdomain.com;
        client_max_body_size 10M;
    
        location / {
            proxy_pass         http://127.0.0.1:8090;
            proxy_http_version 1.1;
            proxy_read_timeout 360s;
            proxy_set_header   Upgrade $http_upgrade;
            proxy_set_header   Connection "upgrade";
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Proto $scheme;
        }
    }

    The Upgrade and Connection headers are required. Without them, WebSocket connections that agents use to communicate with the hub will fail even though the dashboard loads fine.

    Enable the site, test, and obtain SSL
    sudo ln -s /etc/nginx/sites-available/beszel /etc/nginx/sites-enabled/
    sudo nginx -t
    sudo systemctl reload nginx
    
    # Obtain SSL certificate
    sudo certbot --nginx -d monitor.yourdomain.com
    
    # Verify and reload
    sudo nginx -t
    sudo systemctl reload nginx
    5

    Create Your Admin Account and Get Agent Credentials

    Open https://monitor.yourdomain.com in your browser. Beszel will prompt you to create the first admin account.

    After logging in, click Add System in the top right. Fill in:

    • Name: something descriptive, e.g., ramnode-hub
    • Host / IP: the hostname or IP of this server
    • Port: 45876

    Beszel generates a unique public key and token for this agent connection. Copy both values — you need them in the next step.

    Note: Beszel requires HTTPS for the "Copy to clipboard" button to work. If you set up SSL correctly in Step 4, the copy button will function normally.

    6

    Update the Agent and Start It

    Open the Compose file and replace the placeholder values with the credentials from Step 5:

    Update agent environment variables
        environment:
          LISTEN: /beszel_socket/beszel.sock
          HUB_URL: "http://localhost:8090"
          TOKEN: "your-actual-token-here"
          KEY: "ssh-ed25519 AAAA...your-actual-key-here"
    Start the agent
    cd /opt/beszel
    sudo docker compose up -d beszel-agent
    sudo docker logs beszel-agent
    Expected output
    INFO WebSocket connected host=monitor.yourdomain.com

    The system you added should turn green in the dashboard within a few seconds and begin displaying CPU, memory, disk, and network graphs.

    7

    Configure Alerts

    Click Settings (the gear icon) in the top navigation, then go to SMTP to configure outbound email for alerts. RamNode does not restrict outbound SMTP on KVM plans.

    Recommended starting thresholds:

    • CPU: alert when usage exceeds 90% for more than 5 minutes
    • Memory: alert when usage exceeds 85%
    • Disk: alert when usage exceeds 80%
    • Status: alert immediately when the system goes offline
    8

    Add Remote Servers (Optional)

    On the hub dashboard, click Add System and fill in the remote server's IP and a name. Beszel will display a Docker Compose snippet with the KEY and TOKEN pre-filled.

    SSH into the remote server and create a minimal agent Compose file:

    Remote agent setup
    sudo mkdir -p /opt/beszel-agent
    sudo nano /opt/beszel-agent/docker-compose.yml
    Remote agent docker-compose.yml
    services:
      beszel-agent:
        image: henrygd/beszel-agent:latest
        container_name: beszel-agent
        restart: unless-stopped
        network_mode: host
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock:ro
        environment:
          PORT: "45876"
          TOKEN: "your-token"
          KEY: "ssh-ed25519 AAAA..."
    Open the agent port from the hub IP only
    sudo ufw allow from YOUR_HUB_IP to any port 45876 proto tcp
    sudo ufw reload
    
    cd /opt/beszel-agent
    sudo docker compose up -d
    9

    Enable Automatic Backups (Optional)

    Beszel stores all data in ./beszel_data. For automatic off-site backups, navigate to https://monitor.yourdomain.com/_/ (the PocketBase admin panel) and go to Settings → Backups.

    Configure an S3-compatible bucket (Backblaze B2, Wasabi, AWS S3, or RamNode Object Storage). Set the schedule to daily and retain at least 7 copies.

    Upgrading Beszel

    Pull latest and restart
    cd /opt/beszel
    sudo docker compose pull
    sudo docker compose up -d

    Docker Compose will restart only the containers whose images changed. Data in ./beszel_data is preserved because it's mounted as a bind volume.

    Troubleshooting

    Dashboard loads but shows "disconnected" for the local agent

    Check that the TOKEN and KEY in your Compose file exactly match what the hub generated. Even a single extra space will cause authentication to fail. Check agent logs with sudo docker logs beszel-agent.

    Agents on remote servers cannot connect

    Confirm port 45876 is open on the remote server's firewall from the hub IP. Also verify Docker is not blocking the port — run sudo iptables -L DOCKER -n on the remote server.

    nginx returns a 502 Bad Gateway

    The hub container may not be running. Check sudo docker ps and restart with sudo docker compose up -d beszel. Verify the port binding with sudo docker port beszel — it should show 127.0.0.1:8090.

    Disk usage appears incorrect

    If you have multiple mount points, Beszel may not pick them up automatically with Docker. Expose additional filesystems to the agent by adding read-only volume mounts under /extra-filesystems:

    Expose additional mount points
    volumes:
      - /mnt/data/.beszel:/extra-filesystems/data:ro