How CasaOS Implements Its Health Check Service: Architecture and Code Deep Dive

CasaOS implements its health check service through a dedicated HealthService interface that queries systemd for CasaOS service status and scans system ports, exposing the results via REST endpoints at /api/v2/health/services and /api/v2/health/ports.

The health check service in the IceWhaleTech/CasaOS repository provides real-time visibility into internal system services and network port utilization. This subsystem combines Go interface design with external utilities from the CasaOS-Common package to deliver both programmatic and HTTP access to critical health metrics. Understanding this implementation reveals how CasaOS monitors its own operational state through clean abstraction layers and standard Linux interfaces.

Health Service Architecture

The implementation follows a layered architecture consisting of an interface definition, concrete implementation, and HTTP exposure layer.

The HealthService Interface

The contract for health checks is defined in service/health.go through the HealthService interface. This interface declares two primary methods:

  • Services() – Returns a map categorizing CasaOS services by their running status
  • Ports() – Returns slices of occupied TCP and UDP ports

This abstraction allows the health subsystem to remain agnostic of specific implementation details while providing a consistent API for the rest of the application.

Concrete Implementation

The service struct in service/health.go provides the concrete implementation of the HealthService interface. When instantiated, it leverages two critical utilities from the CasaOS-Common package:

  • systemctl.ListServices("casaos*") – Queries systemd for services matching the CasaOS pattern
  • port.ListPortsInUse() – Enumerates open network ports by scanning /proc/net/{tcp,udp} or utilizing netstat

The results are packaged into structures required by the interface, specifically splitting service lists into running and notRunning categories and returning port data as separate integer slices for TCP and UDP.

Service Registration

The health service is wired into the global dependency container in service/service.go. The NewHealthService() factory function creates fresh instances, which are then stored in the Repository struct under the health field. The Health() accessor method makes this service available to HTTP handlers and other consumers throughout the application.

Data Collection Mechanisms

Understanding how CasaOS gathers health data requires examining the interaction between the core service and underlying system utilities.

Querying Systemd Services

The (*service).Services() method invokes systemctl.ListServices("casaos*") from the CasaOS-Common utilities. This helper executes a systemctl query, parses the output, and returns a slice of structures containing Name and Running fields. The implementation then partitions these results into two lists keyed by boolean values: true for running services and false for non-running services.

Scanning Network Ports

For port enumeration, the (*service).Ports() method forwards calls to port.ListPortsInUse(). This CasaOS-Common utility examines the system's network tables to identify occupied TCP and UDP ports, returning them as separate integer slices. A minimal test in service/health_test.go ensures this functionality returns non-empty slices without errors, validating the helper works across different CI environments.

HTTP API Exposure

The REST endpoints exposing health data are implemented in route/v2/health.go. These handlers interact with the service layer through the global MyService repository.

The router registers three primary endpoints:

  • GET /api/v2/health/services – Returns running and stopped CasaOS services via GetHealthServices
  • GET /api/v2/health/ports – Returns occupied TCP and UDP ports via GetHealthPorts
  • GET /api/v2/health/logs – Provides access to system logs (referenced in the route handlers)

Each handler calls MyService.Health() to access the service instance, executes the appropriate method, and marshals the results into OpenAPI-generated response models (HealthServices and HealthPorts). Errors are translated into 500 JSON payloads, while successful responses return structured data matching the API specification.

Practical Usage Examples

Developers can interact with the health check service either programmatically through Go or via HTTP requests.

Consuming the Health Service in Go

To access health data directly from within the CasaOS codebase or external Go packages:

import (
    "github.com/IceWhaleTech/CasaOS/service"
    "log"
)

func main() {
    // Obtain a health service instance
    h := service.NewHealthService()

    // 1️⃣ Get service status
    services, err := h.Services()
    if err != nil {
        log.Fatalf("failed to list services: %v", err)
    }
    log.Printf("Running:   %v", *services[true])
    log.Printf("NotRunning:%v", *services[false])

    // 2️⃣ Get occupied ports
    tcp, udp, err := h.Ports()
    if err != nil {
        log.Fatalf("failed to list ports: %v", err)
    }
    log.Printf("TCP ports: %v", tcp)
    log.Printf("UDP ports: %v", udp)
}

This pattern mirrors the internal usage found in the HTTP handlers.

Accessing Health Endpoints via REST

For external monitoring or debugging, use standard HTTP requests:


# List running and stopped CasaOS services

curl -s http://localhost:80/api/v2/health/services | jq .

# List occupied ports

curl -s http://localhost:80/api/v2/health/ports | jq .

Both endpoints return JSON objects conforming to the OpenAPI definitions (HealthServices and HealthPorts), making them suitable for integration with monitoring systems and automated health checks.

Summary

  • CasaOS exposes health metrics through a dedicated HealthService interface defined in service/health.go, implementing dependency injection patterns for testability.
  • The service queries systemd via systemctl.ListServices("casaos*") to determine which CasaOS services are running or stopped.
  • Port enumeration relies on port.ListPortsInUse() from CasaOS-Common, scanning /proc/net/{tcp,udp} to identify occupied TCP and UDP ports.
  • Global registration occurs in service/service.go through NewHealthService() and the MyService repository, making the health service accessible throughout the application.
  • REST endpoints in route/v2/health.go expose /api/v2/health/services and /api/v2/health/ports, returning OpenAPI-compliant JSON responses suitable for external monitoring tools.

Frequently Asked Questions

How does CasaOS determine if its services are running?

CasaOS queries systemd using the systemctl.ListServices("casaos*") function from the CasaOS-Common package. This utility executes a systemctl command, parses the output to extract service names and their running states, and returns structured data that the HealthService implementation partitions into running and non-running categories.

What network ports does the health check service monitor?

The service monitors all TCP and UDP ports currently in use on the system. The (*service).Ports() method calls port.ListPortsInUse(), which returns two separate integer slices—one for TCP ports and one for UDP ports—by examining the system's network tables or using netstat as a fallback.

Where is the health check service instantiated in the CasaOS codebase?

The health service is instantiated in service/service.go within the NewService() function. This function creates a new HealthService via NewHealthService() and stores it in the global MyService repository, making it accessible through the Health() accessor method used by HTTP handlers in route/v2/health.go.

Can I use the health check service programmatically outside of the HTTP API?

Yes, you can import github.com/IceWhaleTech/CasaOS/service and call NewHealthService() directly to obtain an instance. This allows you to invoke Services() and Ports() programmatically from other Go packages without routing through HTTP, as demonstrated in the usage examples above.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →