Config Backups
    Network Devices

    Deploy Oxidized on a VPS

    Self-host Oxidized on a RamNode VPS for versioned network device configuration backups — Ruby install, device inventory, git output, systemd, and nginx with the web UI.

    Oxidized pulls running-config from network devices (switches, routers, firewalls) and stores versioned backups, usually in a local git repo. Target: Ubuntu 24.04 LTS VPS, 1 vCPU / 1GB RAM is enough for a fleet under a few hundred devices.

    1. Base packages

    shell
    apt update && apt -y upgrade
    apt -y install ruby ruby-dev build-essential git libsqlite3-dev pkg-config \
      libssl-dev nginx

    Check ruby version — Oxidized needs >= 2.7:

    shell
    ruby -v

    2. Create a dedicated user

    shell
    useradd -m -s /bin/bash oxidized
    su - oxidized

    Everything below (until noted) runs as the oxidized user.

    3. Install Oxidized gem

    shell
    gem install --no-document oxidized oxidized-web

    oxidized-web gives you the built-in web UI on port 8888.

    4. Directory layout

    shell
    mkdir -p ~/.config/oxidized

    5. Config file

    ~/.config/oxidized/config:

    shell
    username: oxidized_ro
    password: CHANGE_ME
    model: junos
    interval: 3600
    use_syslog: false
    debug: false
    threads: 30
    timeout: 20
    retries: 3
    prompt: !ruby/regexp /^([\w.@-]+[#>]\s*)$/
    rest: 127.0.0.1:8888
    
    vars:
      enable: CHANGE_ME_ENABLE_SECRET   # for Cisco enable mode if needed
    
    groups: {}
    
    models:
      cisco_ios:
      junos:
      arista_eos:
      fortios:
    
    pid: "/home/oxidized/.config/oxidized/pid"
    
    input:
      default: ssh, telnet
      debug: false
      ssh:
        secure: false
    
    output:
      default: git
      git:
        user: Oxidized
        email: oxidized@ramnode.internal
        repo: "/home/oxidized/.config/oxidized/oxidized.git"
    
    source:
      default: csv
      csv:
        file: "/home/oxidized/.config/oxidized/router.db"
        delimiter: !ruby/regexp /:/
        map:
          name: 0
          model: 1
          username: 2
          password: 3

    Adjust secure: false under SSH only if you need to allow older/weaker ciphers for legacy switches — otherwise leave it enabled.

    6. Device inventory (router.db)

    ~/.config/oxidized/router.db:

    shell
    switch1.ramnode.internal:ios:oxidized_ro:CHANGE_ME
    core-router.ramnode.internal:junos:oxidized_ro:CHANGE_ME
    edge-fw.ramnode.internal:fortios:oxidized_ro:CHANGE_ME

    Format is name:model:username:password (matches the map: in config). For larger fleets, switch the source to sql or write a small script pulling from phpIPAM/NetBox via API instead of hand-editing this file.

    7. First run (foreground, to validate)

    shell
    oxidized

    Confirm it connects to a test device, pulls config, and commits to the git repo at ~/.config/oxidized/oxidized.git.

    8. systemd service

    Back to root:

    shell
    exit

    /etc/systemd/system/oxidized.service:

    shell
    [Unit]
    Description=Oxidized network config backup
    After=network.target
    
    [Service]
    Type=simple
    User=oxidized
    Group=oxidized
    ExecStart=/usr/local/bin/oxidized
    Restart=on-failure
    RestartSec=5
    WorkingDirectory=/home/oxidized
    
    [Install]
    WantedBy=multi-user.target

    Find the actual gem binary path first if /usr/local/bin/oxidized doesn't exist:

    shell
    su - oxidized -c 'which oxidized'

    Use whatever path that returns in the ExecStart line.

    shell
    systemctl daemon-reload
    systemctl enable --now oxidized
    systemctl status oxidized

    9. Reverse proxy for the web UI

    /etc/nginx/sites-available/oxidized:

    shell
    server {
        listen 80;
        server_name oxidized.example.com;
    
        location / {
            proxy_pass http://127.0.0.1:8888;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
    shell
    ln -s /etc/nginx/sites-available/oxidized /etc/nginx/sites-enabled/
    nginx -t && systemctl reload nginx
    certbot --nginx -d oxidized.example.com

    Lock this down further — the web UI has no built-in auth. Either put HTTP basic auth in nginx or bind it to your internal/jump network only:

    shell
    allow 10.0.0.0/8;
    deny all;

    Or with basic auth:

    shell
    apt -y install apache2-utils
    htpasswd -c /etc/nginx/.oxidized-htpasswd admin
    shell
    location / {
        auth_basic "Oxidized";
        auth_basic_user_file /etc/nginx/.oxidized-htpasswd;
        proxy_pass http://127.0.0.1:8888;
    }

    10. Verifying backups

    shell
    cd /home/oxidized/.config/oxidized/oxidized.git
    git log --oneline
    git show HEAD:switch1.ramnode.internal

    11. Nagios/NRPE integration idea

    Since you're already running Nagios 4 across the fleet, a simple check script comparing git log -1 --format=%ct <device> against interval catches devices that have stopped checking in (stale creds, ACL change, device down for scraping).

    Post-install checklist

    • Restrict web UI (basic auth or IP allowlist — no built-in auth)
    • Use a read-only account on every device, not admin/enable creds where avoidable
    • Rotate router.db credentials out of plaintext — consider oxidized-source-sql backed by a credentials vault instead of the flat CSV for anything beyond a lab
    • Confirm SSH host key checking behavior matches your security posture (default trusts on first use)