Skip to main content
Create a complete system monitoring dashboard that combines CPU, memory, disk, network, and process monitoring into a single, continuously updating display.

What You’ll Build

A real-time monitoring dashboard featuring:
  • System overview with host information
  • Live CPU and memory usage meters
  • Disk space monitoring for all partitions
  • Network I/O statistics
  • Top processes by CPU and memory
  • Automatic refresh every few seconds

Prerequisites

1

Install dependencies

go get github.com/shirou/gopsutil/v4
2

Create your project

mkdir system-dashboard
cd system-dashboard
go mod init system-dashboard

Building the Dashboard

1

Import required packages

Create main.go with all necessary imports:
main.go
package main

import (
    "fmt"
    "os"
    "os/exec"
    "runtime"
    "sort"
    "strings"
    "time"

    "github.com/shirou/gopsutil/v4/cpu"
    "github.com/shirou/gopsutil/v4/disk"
    "github.com/shirou/gopsutil/v4/host"
    "github.com/shirou/gopsutil/v4/mem"
    "github.com/shirou/gopsutil/v4/net"
    "github.com/shirou/gopsutil/v4/process"
)
2

Create dashboard state structure

Define structures to hold dashboard data:
main.go
type DashboardData struct {
    Hostname        string
    Platform        string
    Uptime          string
    CPUPercent      float64
    CPUCores        int
    MemoryUsed      uint64
    MemoryTotal     uint64
    MemoryPercent   float64
    SwapUsed        uint64
    SwapTotal       uint64
    SwapPercent     float64
    DiskPartitions  []DiskInfo
    NetworkStats    []NetworkInfo
    TopProcesses    []ProcessInfo
    LastUpdate      time.Time
}

type DiskInfo struct {
    Device      string
    Mountpoint  string
    Total       uint64
    Used        uint64
    Free        uint64
    UsedPercent float64
}

type NetworkInfo struct {
    Interface   string
    BytesSent   uint64
    BytesRecv   uint64
    PacketsSent uint64
    PacketsRecv uint64
}

type ProcessInfo struct {
    PID        int32
    Name       string
    CPUPercent float64
    MemPercent float32
    MemoryMB   uint64
}
3

Implement data collection functions

Create functions to gather system metrics:
main.go
func collectSystemInfo() (hostname, platform, uptime string) {
    hostInfo, err := host.Info()
    if err == nil {
        hostname = hostInfo.Hostname
        platform = fmt.Sprintf("%s %s", hostInfo.Platform, hostInfo.PlatformVersion)
    }

    uptimeSecs, err := host.Uptime()
    if err == nil {
        d := time.Duration(uptimeSecs) * time.Second
        days := d / (24 * time.Hour)
        d -= days * 24 * time.Hour
        hours := d / time.Hour
        d -= hours * time.Hour
        minutes := d / time.Minute
        uptime = fmt.Sprintf("%dd %dh %dm", days, hours, minutes)
    }

    return
}

func collectCPUInfo() (percent float64, cores int) {
    percentages, err := cpu.Percent(time.Second, false)
    if err == nil && len(percentages) > 0 {
        percent = percentages[0]
    }

    cores, _ = cpu.Counts(true)
    return
}

func collectMemoryInfo() (used, total uint64, percent float64, 
                          swapUsed, swapTotal uint64, swapPercent float64) {
    vmStat, err := mem.VirtualMemory()
    if err == nil {
        used = vmStat.Used
        total = vmStat.Total
        percent = vmStat.UsedPercent
    }

    swapStat, err := mem.SwapMemory()
    if err == nil {
        swapUsed = swapStat.Used
        swapTotal = swapStat.Total
        swapPercent = swapStat.UsedPercent
    }

    return
}

func collectDiskInfo() []DiskInfo {
    var disks []DiskInfo

    partitions, err := disk.Partitions(false)
    if err != nil {
        return disks
    }

    for _, partition := range partitions {
        usageStat, err := disk.Usage(partition.Mountpoint)
        if err != nil {
            continue
        }

        disks = append(disks, DiskInfo{
            Device:      partition.Device,
            Mountpoint:  partition.Mountpoint,
            Total:       usageStat.Total,
            Used:        usageStat.Used,
            Free:        usageStat.Free,
            UsedPercent: usageStat.UsedPercent,
        })
    }

    return disks
}

func collectNetworkInfo() []NetworkInfo {
    var networks []NetworkInfo

    ioCounters, err := net.IOCounters(true)
    if err != nil {
        return networks
    }

    for _, counter := range ioCounters {
        // Skip loopback and interfaces with no activity
        if counter.Name == "lo" || 
           (counter.BytesSent == 0 && counter.BytesRecv == 0) {
            continue
        }

        networks = append(networks, NetworkInfo{
            Interface:   counter.Name,
            BytesSent:   counter.BytesSent,
            BytesRecv:   counter.BytesRecv,
            PacketsSent: counter.PacketsSent,
            PacketsRecv: counter.PacketsRecv,
        })
    }

    return networks
}

func collectTopProcesses(limit int) []ProcessInfo {
    var processes []ProcessInfo

    pids, err := process.Pids()
    if err != nil {
        return processes
    }

    for _, pid := range pids {
        p, err := process.NewProcess(pid)
        if err != nil {
            continue
        }

        name, err := p.Name()
        if err != nil {
            continue
        }

        cpuPercent, _ := p.CPUPercent()
        memPercent, _ := p.MemoryPercent()
        memInfo, _ := p.MemoryInfo()

        var memMB uint64
        if memInfo != nil {
            memMB = memInfo.RSS / 1024 / 1024
        }

        processes = append(processes, ProcessInfo{
            PID:        pid,
            Name:       name,
            CPUPercent: cpuPercent,
            MemPercent: memPercent,
            MemoryMB:   memMB,
        })
    }

    // Sort by CPU usage
    sort.Slice(processes, func(i, j int) bool {
        return processes[i].CPUPercent > processes[j].CPUPercent
    })

    if len(processes) > limit {
        processes = processes[:limit]
    }

    return processes
}
4

Create the main collection function

Combine all collectors into one function:
main.go
func collectDashboardData() DashboardData {
    data := DashboardData{
        LastUpdate: time.Now(),
    }

    // System info
    data.Hostname, data.Platform, data.Uptime = collectSystemInfo()

    // CPU info
    data.CPUPercent, data.CPUCores = collectCPUInfo()

    // Memory info
    data.MemoryUsed, data.MemoryTotal, data.MemoryPercent,
        data.SwapUsed, data.SwapTotal, data.SwapPercent = collectMemoryInfo()

    // Disk info
    data.DiskPartitions = collectDiskInfo()

    // Network info
    data.NetworkStats = collectNetworkInfo()

    // Top processes
    data.TopProcesses = collectTopProcesses(10)

    return data
}
5

Create display formatting functions

Add functions to format and display the data:
main.go
func clearScreen() {
    if runtime.GOOS == "windows" {
        cmd := exec.Command("cmd", "/c", "cls")
        cmd.Stdout = os.Stdout
        cmd.Run()
    } else {
        cmd := exec.Command("clear")
        cmd.Stdout = os.Stdout
        cmd.Run()
    }
}

func formatBytes(bytes uint64) string {
    const unit = 1024
    if bytes < unit {
        return fmt.Sprintf("%d B", bytes)
    }
    div, exp := uint64(unit), 0
    for n := bytes / unit; n >= unit; n /= unit {
        div *= unit
        exp++
    }
    return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
}

func createProgressBar(percent float64, width int) string {
    filled := int(percent / 100 * float64(width))
    if filled > width {
        filled = width
    }

    bar := strings.Repeat("█", filled)
    empty := strings.Repeat("░", width-filled)
    return fmt.Sprintf("[%s%s] %.1f%%", bar, empty, percent)
}

func displayDashboard(data DashboardData) {
    clearScreen()

    fmt.Println("╔═══════════════════════════════════════════════════════════════════════════════╗")
    fmt.Println("║                        SYSTEM MONITORING DASHBOARD                            ║")
    fmt.Println("╚═══════════════════════════════════════════════════════════════════════════════╝")
    fmt.Printf("\nLast Update: %s\n\n", data.LastUpdate.Format("2006-01-02 15:04:05"))

    // System Information
    fmt.Println("┌─ SYSTEM INFORMATION ─────────────────────────────────────────────────────────┐")
    fmt.Printf("│ Hostname: %-66s\n", data.Hostname)
    fmt.Printf("│ Platform: %-66s\n", data.Platform)
    fmt.Printf("│ Uptime:   %-66s\n", data.Uptime)
    fmt.Println("└──────────────────────────────────────────────────────────────────────────────┘")

    // CPU Information
    fmt.Println("\n┌─ CPU ────────────────────────────────────────────────────────────────────────┐")
    fmt.Printf("│ Cores: %d\n", data.CPUCores)
    fmt.Printf("│ Usage: %s\n", createProgressBar(data.CPUPercent, 50))
    fmt.Println("└──────────────────────────────────────────────────────────────────────────────┘")

    // Memory Information
    fmt.Println("\n┌─ MEMORY ─────────────────────────────────────────────────────────────────────┐")
    fmt.Printf("│ RAM:  %s / %s\n", 
        formatBytes(data.MemoryUsed), formatBytes(data.MemoryTotal))
    fmt.Printf("│       %s\n", createProgressBar(data.MemoryPercent, 50))
    fmt.Printf("│\n")
    fmt.Printf("│ Swap: %s / %s\n", 
        formatBytes(data.SwapUsed), formatBytes(data.SwapTotal))
    fmt.Printf("│       %s\n", createProgressBar(data.SwapPercent, 50))
    fmt.Println("└──────────────────────────────────────────────────────────────────────────────┘")

    // Disk Information
    fmt.Println("\n┌─ DISK USAGE ─────────────────────────────────────────────────────────────────┐")
    for _, disk := range data.DiskPartitions {
        fmt.Printf("│ %s (%s)\n", disk.Mountpoint, disk.Device)
        fmt.Printf("│ %s / %s\n", 
            formatBytes(disk.Used), formatBytes(disk.Total))
        fmt.Printf("│ %s\n", createProgressBar(disk.UsedPercent, 50))
        fmt.Printf("│\n")
    }
    fmt.Println("└──────────────────────────────────────────────────────────────────────────────┘")

    // Network Information
    if len(data.NetworkStats) > 0 {
        fmt.Println("\n┌─ NETWORK I/O ────────────────────────────────────────────────────────────────┐")
        fmt.Printf("│ %-15s %-15s %-15s %-15s %-12s\n", 
            "Interface", "Sent", "Received", "Packets Out", "Packets In")
        fmt.Printf("│ %s\n", strings.Repeat("─", 78))
        for _, net := range data.NetworkStats {
            fmt.Printf("│ %-15s %-15s %-15s %-15d %-12d\n",
                net.Interface,
                formatBytes(net.BytesSent),
                formatBytes(net.BytesRecv),
                net.PacketsSent,
                net.PacketsRecv,
            )
        }
        fmt.Println("└──────────────────────────────────────────────────────────────────────────────┘")
    }

    // Top Processes
    fmt.Println("\n┌─ TOP PROCESSES (by CPU) ─────────────────────────────────────────────────────┐")
    fmt.Printf("│ %-8s %-25s %-10s %-10s %-12s\n", 
        "PID", "Name", "CPU %", "Mem %", "Memory")
    fmt.Printf("│ %s\n", strings.Repeat("─", 78))
    for _, proc := range data.TopProcesses {
        name := proc.Name
        if len(name) > 25 {
            name = name[:22] + "..."
        }
        fmt.Printf("│ %-8d %-25s %-10.2f %-10.2f %-12s\n",
            proc.PID,
            name,
            proc.CPUPercent,
            proc.MemPercent,
            formatBytes(proc.MemoryMB*1024*1024),
        )
    }
    fmt.Println("└──────────────────────────────────────────────────────────────────────────────┘")

    fmt.Println("\nPress Ctrl+C to exit")
}
6

Create the main loop

Implement the main function with continuous refresh:
main.go
func main() {
    fmt.Println("Starting System Dashboard...")
    fmt.Println("Loading data...\n")
    time.Sleep(1 * time.Second)

    // Set up refresh ticker
    ticker := time.NewTicker(3 * time.Second)
    defer ticker.Stop()

    // Initial display
    data := collectDashboardData()
    displayDashboard(data)

    // Update loop
    for range ticker.C {
        data = collectDashboardData()
        displayDashboard(data)
    }
}
7

Run the dashboard

Start your monitoring dashboard:
go run main.go
The dashboard will refresh every 3 seconds automatically.

Example Output

╔═══════════════════════════════════════════════════════════════════════════════╗
║                        SYSTEM MONITORING DASHBOARD                            ║
╚═══════════════════════════════════════════════════════════════════════════════╝

Last Update: 2024-03-09 15:30:45

┌─ SYSTEM INFORMATION ─────────────────────────────────────────────────────────┐
│ Hostname: my-server                                                          │
│ Platform: ubuntu 22.04                                                       │
│ Uptime:   5d 12h 30m                                                         │
└──────────────────────────────────────────────────────────────────────────────┘

┌─ CPU ────────────────────────────────────────────────────────────────────────┐
│ Cores: 8
│ Usage: [███████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 23.4%
└──────────────────────────────────────────────────────────────────────────────┘

┌─ MEMORY ─────────────────────────────────────────────────────────────────────┐
│ RAM:  8.2 GB / 16.0 GB
│       [█████████████████████████░░░░░░░░░░░░░░░░░░░░░░░░░] 51.2%

│ Swap: 0 B / 4.0 GB
│       [░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0.0%
└──────────────────────────────────────────────────────────────────────────────┘

┌─ TOP PROCESSES (by CPU) ─────────────────────────────────────────────────────┐
│ PID      Name                      CPU %      Mem %      Memory      │
│ ──────────────────────────────────────────────────────────────────────────────│
│ 1234     chrome                    45.32      8.50       1.0 GB      │
│ 5678     node                      12.45      4.20       512.0 MB    │
│ 9012     docker                    8.90       3.10       400.0 MB    │
└──────────────────────────────────────────────────────────────────────────────┘

Press Ctrl+C to exit

Enhancements

Consider adding these features to your dashboard:

Historical Data

Store historical metrics to show trends and create graphs.

Alerts

Add threshold alerts for high CPU, memory, or disk usage.

Export Data

Save metrics to CSV or JSON for analysis.

Web Interface

Create an HTTP server to display the dashboard in a browser.

Key Features

Real-time Updates

Dashboard refreshes automatically every 3 seconds with live data.

Comprehensive View

Monitor all system aspects: CPU, memory, disk, network, and processes.

Visual Indicators

Progress bars provide quick visual understanding of resource usage.

Process Ranking

Automatically shows top resource-consuming processes.
For production use, consider:
  • Adding graceful shutdown handling
  • Implementing error recovery for data collection failures
  • Adding configuration options for refresh rate
  • Creating custom views for different monitoring needs

Basic Monitoring

Learn the fundamentals of system monitoring.

Process Monitoring

Dive deeper into process management and monitoring.

Build docs developers (and LLMs) love