{"id":37158852,"url":"https://github.com/axondata/go-svcmgr","last_synced_at":"2026-01-16T23:24:15.582Z","repository":{"id":314040444,"uuid":"1052715502","full_name":"axondata/go-svcmgr","owner":"axondata","description":"Pure Go client library for runit service supervision - control, monitor, and manage runit services programmatically","archived":false,"fork":false,"pushed_at":"2026-01-12T02:52:32.000Z","size":178,"stargazers_count":2,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-14T20:43:49.575Z","etag":null,"topics":["golang-library","process-manager","runit","runit-service","runit-sv-addons"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/axondata.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-08T12:58:17.000Z","updated_at":"2026-01-06T15:54:20.000Z","dependencies_parsed_at":"2025-09-10T09:03:34.957Z","dependency_job_id":null,"html_url":"https://github.com/axondata/go-svcmgr","commit_stats":null,"previous_names":["axondata/go-svcmgr"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/axondata/go-svcmgr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/axondata%2Fgo-svcmgr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/axondata%2Fgo-svcmgr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/axondata%2Fgo-svcmgr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/axondata%2Fgo-svcmgr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/axondata","download_url":"https://codeload.github.com/axondata/go-svcmgr/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/axondata%2Fgo-svcmgr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28487586,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T22:54:02.790Z","status":"ssl_error","status_checked_at":"2026-01-16T22:50:10.344Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang-library","process-manager","runit","runit-service","runit-sv-addons"],"created_at":"2026-01-14T18:59:08.820Z","updated_at":"2026-01-16T23:24:15.540Z","avatar_url":"https://github.com/axondata.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `go-svcmgr`\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/axondata/go-svcmgr.svg)](https://pkg.go.dev/github.com/axondata/go-svcmgr)\n[![Go Report Card](https://goreportcard.com/badge/github.com/axondata/go-svcmgr)](https://goreportcard.com/report/github.com/axondata/go-svcmgr)\n[![Coverage Status](https://coveralls.io/repos/github/axondata/go-svcmgr/badge.svg?branch=main)](https://coveralls.io/github/axondata/go-svcmgr?branch=main)\n[![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![GitHub release](https://img.shields.io/github/release/axondata/go-svcmgr.svg)](https://github.com/axondata/go-svcmgr/releases)\n\nA Go-native library for controlling process supervisors including [`runit`](https://github.com/g-pape/runit/), [`s6`](https://github.com/skarnet/s6), and other [`daemontools`](https://cr.yp.to/daemontools.html)-compatible systems, with an adapter for [`systemd`](https://systemd.io/) on Linux.\n\n## Features\n\n- **Native daemontools protocol**: Direct binary control via `supervise/control` and `supervise/status`\n- **systemd adapter**: Unified API for systemd services on Linux\n- **Real-time monitoring**: Status changes via fsnotify (no polling)\n- **Concurrent operations**: Worker pool management via [`Manager`](https://pkg.go.dev/github.com/axondata/go-svcmgr#Manager)\n- **Zero allocations**: Optimized hot paths with stack-based operations\n- **Cross-platform**: Linux and macOS (systemd on Linux only)\n- **Development mode**: Unprivileged `runsvdir` trees for testing\n- **Multi-supervisor support**: [`runit`](https://github.com/g-pape/runit/), [`s6`](https://github.com/skarnet/s6), [`daemontools`](https://cr.yp.to/daemontools.html), and [`systemd`](https://systemd.io/)\n\n## Installation\n\n```bash\ngo get github.com/axondata/go-svcmgr\n```\n\nOptional build tags:\n- `fsnotify` - Enable file watching (recommended)\n- `devtree_cmd` - Enable dev tree helpers for spawning runsvdir\n- `sv_fallback` - Enable text-based status fallback (testing only)\n\n## Quick Start\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"log\"\n    \"sync\"\n    \"time\"\n\n    \"github.com/axondata/go-svcmgr\"\n)\n\nfunc main() {\n    // Create client for a service\n    client, err := svcmgr.New(\"/etc/service/web\")\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n    defer cancel()\n\n    var wg sync.WaitGroup\n\n    // Start watching in a goroutine\n    wg.Add(1)\n    go func() {\n        defer wg.Done()\n\n        events, stop, err := client.Watch(ctx)\n        if err != nil {\n            log.Printf(\"Watch error: %v\", err)\n            return\n        }\n        defer stop()\n\n        for event := range events {\n            if event.Err != nil {\n                log.Printf(\"Event error: %v\", event.Err)\n                continue\n            }\n            log.Printf(\"State changed: %v (PID: %d)\",\n                event.Status.State, event.Status.PID)\n        }\n    }()\n\n    // Control the service in another goroutine\n    wg.Add(1)\n    go func() {\n        defer wg.Done()\n\n        // Give watcher time to start\n        time.Sleep(100 * time.Millisecond)\n\n        // Stop the service\n        log.Println(\"Stopping service...\")\n        if err := client.Down(ctx); err != nil {\n            log.Printf(\"Down error: %v\", err)\n            return\n        }\n\n        time.Sleep(2 * time.Second)\n\n        // Start the service\n        log.Println(\"Starting service...\")\n        if err := client.Up(ctx); err != nil {\n            log.Printf(\"Up error: %v\", err)\n            return\n        }\n\n        time.Sleep(2 * time.Second)\n\n        // Get final status\n        status, err := client.Status(ctx)\n        if err != nil {\n            log.Printf(\"Status error: %v\", err)\n            return\n        }\n\n        fmt.Printf(\"Final state: %v, PID: %d, Uptime: %s\\n\",\n            status.State, status.PID, status.Uptime)\n\n        // Cancel context to stop watcher\n        cancel()\n    }()\n\n    wg.Wait()\n}\n```\n\n## API Reference\n\n### [`Client`](https://pkg.go.dev/github.com/axondata/go-svcmgr#Client) (Single Service)\n\n```go\n// Create a client\nclient, err := svcmgr.New(\"/etc/service/myapp\",\n    svcmgr.WithDialTimeout(3*time.Second),\n    svcmgr.WithMaxAttempts(5),\n    svcmgr.WithBackoff(10*time.Millisecond, 1*time.Second),\n)\n\n// Control commands\nclient.Up(ctx)            // Start service (send 'u')\nclient.Down(ctx)          // Stop service (send 'd')\nclient.Once(ctx)          // Run once (send 'o')\nclient.Term(ctx)          // Send SIGTERM (send 't')\nclient.Kill(ctx)          // Send SIGKILL (send 'k')\nclient.HUP(ctx)           // Send SIGHUP (send 'h')\nclient.Interrupt(ctx)     // Send SIGINT (send 'i')\nclient.Alarm(ctx)         // Send SIGALRM (send 'a')\nclient.Quit(ctx)          // Send SIGQUIT (send 'q')\nclient.Pause(ctx)         // Send SIGSTOP (send 'p')\nclient.Cont(ctx)          // Send SIGCONT (send 'c')\nclient.ExitSupervise(ctx) // Exit supervise (send 'x')\n\n// Get status\nstatus, err := client.Status(ctx)\n```\n\n### Status Structure\n\nSee [`Status`](https://pkg.go.dev/github.com/axondata/go-svcmgr#Status) and [`State`](https://pkg.go.dev/github.com/axondata/go-svcmgr#State) types in the API documentation.\n\n### [`Manager`](https://pkg.go.dev/github.com/axondata/go-svcmgr#Manager) (Multiple Services)\n\n```go\n// Create manager with worker pool\nmgr := svcmgr.NewManager(\n    svcmgr.WithConcurrency(10),\n    svcmgr.WithTimeout(5*time.Second),\n)\n\n// Bulk operations\nservices := []string{\n    \"/etc/service/web\",\n    \"/etc/service/db\",\n    \"/etc/service/cache\",\n}\n\n// Start all services\nerr := mgr.Up(ctx, services...)\n\n// Get all statuses\nstatuses, err := mgr.Status(ctx, services...)\nfor svc, status := range statuses {\n    fmt.Printf(\"%s: %v (PID %d)\\n\", svc, status.State, status.PID)\n}\n\n// Stop all services\nerr = mgr.Down(ctx, services...)\n```\n\n### [`DevTree`](https://pkg.go.dev/github.com/axondata/go-svcmgr#DevTree) (Development Mode)\n\nBuild with `-tags devtree_cmd` to enable:\n\n```go\n// Create dev tree\ntree, err := svcmgr.NewDevTree(\"/tmp/my-runit\")\nif err != nil {\n    log.Fatal(err)\n}\n\n// Initialize directories\nerr = tree.Ensure()\n\n// Start runsvdir\nerr = tree.EnsureRunsvdir()\n\n// Enable a service\nerr = tree.EnableService(\"myapp\")\n\n// Disable a service\nerr = tree.DisableService(\"myapp\")\n```\n\n### [`ServiceBuilder`](https://pkg.go.dev/github.com/axondata/go-svcmgr#ServiceBuilder)\n\n```go\nbuilder := svcmgr.NewServiceBuilder(\"myapp\", \"/tmp/services\")\n\nbuilder.\n    WithCmd([]string{\"/usr/bin/myapp\", \"--port\", \"8080\"}).\n    WithCwd(\"/var/myapp\").\n    WithEnv(\"NODE_ENV\", \"production\").\n    WithChpst(func(c *svcmgr.ChpstBuilder) { // See https://pkg.go.dev/github.com/axondata/go-svcmgr#ChpstBuilder\n        c.User = \"myapp\"\n        c.LimitMem = 1024 * 1024 * 512  // 512MB\n        c.LimitFiles = 1024\n    }).\n    WithSvlogd(func(s *svcmgr.SvlogdBuilder) { // See https://pkg.go.dev/github.com/axondata/go-svcmgr#SvlogdBuilder\n        s.Size = 10000000  // 10MB per file\n        s.Num = 10         // Keep 10 files\n    })\n\nerr := builder.Build()\n```\n\n## Compatibility with daemontools and s6\n\nThis library works with any daemontools-compatible supervision system, including:\n- **runit** - Full support for all operations\n- **daemontools** - Compatible except for `Once()` and `Quit()` operations\n- **s6** - Full compatibility with all operations\n\nThe library provides factory functions for each system (see [compatibility functions](https://pkg.go.dev/github.com/axondata/go-svcmgr#ConfigRunit)):\n\n```go\n// For runit\nconfig := svcmgr.ConfigRunit()\nclient, err := svcmgr.NewClientWithConfig(\"/etc/service/myapp\", config)\n\n// For daemontools\nconfig := svcmgr.ConfigDaemontools()\nclient, err := svcmgr.NewClientWithConfig(\"/service/myapp\", config)\n\n// For s6\nconfig := svcmgr.ConfigS6()\nclient, err := svcmgr.NewClientWithConfig(\"/run/service/myapp\", config)\n\n// Service builders for each system\nrunitBuilder := svcmgr.ServiceBuilderRunit(\"myapp\", \"/etc/service\")        // See https://pkg.go.dev/github.com/axondata/go-svcmgr#ServiceBuilderRunit\ndtBuilder := svcmgr.ServiceBuilderDaemontools(\"myapp\", \"/service\")         // See https://pkg.go.dev/github.com/axondata/go-svcmgr#ServiceBuilderDaemontools\ns6Builder := svcmgr.ServiceBuilderS6(\"myapp\", \"/run/service\")              // See https://pkg.go.dev/github.com/axondata/go-svcmgr#ServiceBuilderS6\n```\n\n### systemd Adapter (Linux only)\n\nWhile systemd uses a different architecture than daemontools-family supervisors, this library provides an adapter that offers a consistent API:\n\n```go\n// Create a service builder\nbuilder := svcmgr.NewServiceBuilder(\"myapp\", \"\")\nbuilder.WithCmd([]string{\"/usr/bin/myapp\", \"--config\", \"/etc/myapp.conf\"})\nbuilder.WithCwd(\"/var/lib/myapp\")\nbuilder.WithEnv(\"ENV_VAR\", \"value\")\nbuilder.WithChpst(func(c *svcmgr.ChpstConfig) {\n    c.User = \"myuser\"\n    c.LimitMem = 1024*1024*1024  // 1GB\n})\n\n// Generate and install systemd unit file\nsystemdBuilder := svcmgr.NewBuilderSystemd(builder)\nif err := systemdBuilder.Build(); err != nil {\n    log.Fatal(err)\n}\n\n// Control the service\nclient := svcmgr.NewClientSystemd(\"myapp\")\nif err := client.Start(context.Background()); err != nil {\n    log.Fatal(err)\n}\n\n// Send signals\nclient.USR1(ctx)  // Send SIGUSR1 to main process\nclient.Term(ctx)  // Send SIGTERM to main process\n```\n\nKey features:\n- Generates native systemd unit files from `ServiceBuilder` configurations\n- Maps process limits and environment variables to systemd directives\n- Translates operations to appropriate systemctl commands\n- Sends signals directly to MainPID for precise control\n- Automatic sudo handling for non-root users\n\n### Differences between systems\n\n| Feature | runit | daemontools | s6 | systemd |\n|---------|-------|-------------|-----|---------|\n| Default path | `/etc/service` | `/service` | `/run/service` | `/etc/systemd/system` |\n| Privilege tool | `chpst` | `setuidgid` | `s6-setuidgid` | Unit directives |\n| Logger | `svlogd` | `multilog` | `s6-log` | `journald` |\n| Scanner | `runsvdir` | `svscan` | `s6-svscan` | `systemd` (PID 1) |\n| Control method | Binary protocol | Binary protocol | Binary protocol | D-Bus/systemctl |\n| `Once()` support | ✓ | ✗ | ✓ | ✓ (via systemd-run) |\n| `Quit()` support | ✓ | ✗ | ✓ | ✓ |\n| `USR1/USR2` support | ✓ | ✓ | ✓ | ✓ |\n| Platform | Unix-like | Unix-like | Unix-like | Linux only |\n\nThe daemontools family (runit, daemontools, s6) share a common binary protocol for `supervise/control` and `supervise/status`. The systemd adapter translates the same operations to systemctl commands and generates native unit files from shared service configurations.\n\n## Control Commands Reference\n\n| Method | Byte | Signal | Description | runit | daemontools | s6 | systemd |\n|--------|------|--------|-------------|-------|-------------|-----|---------|\n| `Up()` / `Start()` | `u` | - | Start service (want up) | ✓ | ✓ | ✓ | ✓ |\n| `Once()` | `o` | - | Run service once | ✓ | ✗ | ✓ | ✓ |\n| `Down()` / `Stop()` | `d` | - | Stop service (want down) | ✓ | ✓ | ✓ | ✓ |\n| `Restart()` | - | - | Stop then start service | ✓ | ✓ | ✓ | ✓ |\n| `Term()` | `t` | SIGTERM | Graceful termination | ✓ | ✓ | ✓ | ✓ |\n| `Interrupt()` | `i` | SIGINT | Interrupt | ✓ | ✓ | ✓ | ✓ |\n| `HUP()` | `h` | SIGHUP | Reload configuration | ✓ | ✓ | ✓ | ✓ |\n| `Alarm()` | `a` | SIGALRM | Alarm signal | ✓ | ✓ | ✓ | ✓ |\n| `Quit()` | `q` | SIGQUIT | Quit with core dump | ✓ | ✗ | ✓ | ✓ |\n| `USR1()` | `1` | SIGUSR1 | User signal 1 | ✓ | ✓ | ✓ | ✓ |\n| `USR2()` | `2` | SIGUSR2 | User signal 2 | ✓ | ✓ | ✓ | ✓ |\n| `Kill()` | `k` | SIGKILL | Force kill | ✓ | ✓ | ✓ | ✓ |\n| `Pause()` | `p` | SIGSTOP | Pause process | ✓ | ✓ | ✓ | ✓ |\n| `Cont()` | `c` | SIGCONT | Continue process | ✓ | ✓ | ✓ | ✓ |\n| `ExitSupervise()` | `x` | - | Terminate supervise | ✓ | ✓ | ✓ | N/A |\n\n## Status Binary Format\n\nThe 20-byte `supervise/status` record:\n\n```\nBytes 0-7:   TAI64N seconds (big-endian uint64)\nBytes 8-11:  TAI64N nanoseconds (big-endian uint32)\nBytes 12-15: PID (big-endian uint32)\nByte 16:     Paused flag (non-zero = paused)\nByte 17:     Want flag ('u' = up, 'd' = down)\nByte 18:     Term flag (non-zero = TERM sent)\nByte 19:     Run flag (non-zero = normally up)\n```\n\n## Error Handling\n\nThe library provides typed errors. See [`OpError`](https://pkg.go.dev/github.com/axondata/go-svcmgr#OpError) and the error variables in the [API documentation](https://pkg.go.dev/github.com/axondata/go-svcmgr#pkg-variables).\n\n## Testing\n\n### Unit Tests\n\nRun the standard unit tests:\n\n```bash\ngo test ./...\n```\n\n### Integration Tests\n\nThe library includes integration tests for different supervision systems. Each requires the respective tools to be installed.\n\n#### Runit Integration Tests\n\nTests for runit require `runsv` and `runsvdir` to be installed:\n\n```bash\n# Run all runit integration tests\ngo test -tags=integration -v ./...\n\n# Or explicitly for runit\ngo test -tags=integration_runit -v ./...\n\n# Run a specific integration test\ngo test -tags=integration -v -run TestIntegrationSingleService\n```\n\n#### Daemontools Integration Tests\n\nTests for daemontools require `svscan` and `supervise` to be installed:\n\n```bash\n# Run daemontools integration tests\ngo test -tags=integration_daemontools -v ./...\n```\n\n#### S6 Integration Tests\n\nTests for s6 require `s6-svscan` and `s6-supervise` to be installed:\n\n```bash\n# Run s6 integration tests\ngo test -tags=integration_s6 -v ./...\n```\n\nThe runit integration tests cover:\n- Service lifecycle (start, stop, restart)\n- Signal handling (TERM, HUP, etc.)\n- Status monitoring and state transitions\n- Watch functionality with fsnotify\n- Services with different exit codes\n- ServiceBuilder generated services\n\n## Performance\n\nBenchmarks on Apple M3 Pro (2025-09-08):\n\n```\nBenchmarkStatusDecode-12          32006547\t 37.64 ns/op   0 B/op  0 allocs/op\nBenchmarkStatusDecodeParallel-12  187062128\t  9.825 ns/op  0 B/op  0 allocs/op\nBenchmarkDecodeStatus-12          31235832\t 37.89 ns/op   0 B/op  0 allocs/op\n```\n\n- **Status decode**: ~38ns/op with zero allocations\n- **Parallel decode**: ~10ns/op when running concurrently\n- **State/Op strings**: \u003c1ns/op with zero allocations\n- **Control send**: Sub-millisecond for local sockets\n- **Watch events**: Debounced at 25ms by default (configurable)\n\n## Examples\n\nSee the `examples/` directory for complete examples:\n\n- `examples/basic/` - Simple service control\n- `examples/watch/` - Real-time status monitoring\n- `examples/manager/` - Bulk service operations\n- `examples/compat/` - Using with daemontools and s6\n- `examples/devtree/` - Development environment setup\n\n## Requirements\n\n- Go 1.21+\n- [`runit`](https://github.com/g-pape/runit/), [`s6`](https://github.com/skarnet/s6), or any [`daemontools`](https://cr.yp.to/daemontools.html)-compatible process supervisor.\n- Linux or macOS\n\n## License\n\nApache 2.0 - See [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faxondata%2Fgo-svcmgr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faxondata%2Fgo-svcmgr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faxondata%2Fgo-svcmgr/lists"}