# go-daemon: turning a Go program into a background process without fork

> A small library that daemonizes by re-executing itself with an environment marker, because calling fork in a Go runtime would lose the goroutines you already have.

**sevlyar/go-daemon** — A library for writing system daemons in golang.

- Repository: https://github.com/sevlyar/go-daemon
- Stars: 2,309 · Forks: 255
- Language: Go
- License: MIT
- Published: 2026-10-07 · Updated: 2026-10-07 · Language: en
- Canonical page: https://hysenlabs.com/projects/sevlyar-go-daemon

## Why not just call fork

The README opens with the reason this library exists, and it is a Go-specific problem rather than a general one. The fork syscall cannot be used in a Go runtime, because a child process created that way does not inherit the parent's threads and goroutines. In a language with a single thread, fork is an ordinary way to background a process. In Go, which is built around an OS thread pool and thousands of goroutines, forking gives you a child with a fraction of the runtime you had, and the parent already running anything concurrent.

The workaround is not to fork at all. go-daemon re-executes the program's own binary with a predefined environment variable set, and treats the presence of that variable as the signal that it is now the second copy:

```go
import "log"

func main() {
	Pre()

	context := new(Context)
	child, _ := context.Reborn()

	if child != nil {
		PostParent()
	} else {
		defer func() {
			if err := context.Release(); err != nil {
				log.Printf("Unable to release pid-file: %s", err.Error())
			}
		}()

		PostChild()
	}
}
```

Pre is called unconditionally at the top of main. Reborn then either spawns the child copy and returns a non-nil value in the parent, or returns nil in the child. The branch is the whole mechanism, and everything after PostParent or PostChild is your own program running in one role or the other.

The README pairs this with a diagram of the two-process relationship, which is the fastest way to understand the model if the code alone leaves you guessing.

## Pid files are handled for you, which is the part you would otherwise get wrong

A Context carries the state that makes this more than a process re-exec, most importantly the pid file. The library advertises out of the box work with pid files and goroutine-safe daemonization, and the deferred Release in the example above is what removes the pid file on a clean shutdown.

The features list is short and specific: goroutine-safe daemonization, out of the box work with pid files, easy handling of system signals, and control of a daemon. Each of those is a thing that daemonization libraries traditionally get subtly wrong, because each requires coordinating process state that is shared between two copies of your program.

Signal handling is the one worth planning for. A daemonized process detaches from the terminal, so there is no Ctrl-C to stop it and no terminal to report to. Your shutdown path has to come from a signal, which is what the library's signal support is for, and the release notes show it has been extended over time.

The tree gives a good picture of the platform matrix. There are separate implementations for Unix, plus a stub, and the lock file implementation has its own Unix, Solaris and stub variants. There is also a dedicated build file for Solaris, added in the v0.1.5 release, which is a good sign about how seriously the platform support is taken.

## Three examples that each solve a different problem

Rather than expanding the prose documentation, the README points at an examples directory with three programs, each named for the operational problem it addresses:

- Simple
- Log rotation
- Signal handling

That list is a description of what daemonization actually costs in production. Starting a process in the background is the easy part. Keeping its log from filling a disk, and being able to stop it cleanly, are the parts that get missed.

Log rotation is the one that separates a real daemon from a demo, and having a worked example means you can see how the child process is expected to reopen its log file rather than holding a descriptor to a file that has since been rotated away.

Signal handling is the counterpart, covering the shutdown path that a backgrounded process needs and an interactive one does not.

The Simple example is where to start, because it isolates the Pre, Reborn, PostParent, PostChild sequence with nothing else in the way.

## The install instructions predate Go modules, and the author says not to rely on the API

Two things in the installation section deserve a flag. First, the recommended commands are from the GOPATH era:

```bash
go get github.com/sevlyar/go-daemon
```

An alternative using gopkg.in is also offered:

```bash
go get gopkg.in/sevlyar/go-daemon.v0
```

Neither is how you add a dependency in a module-aware project today. The repository does have a go.mod and a go.sum, so it is module-compatible, but the README was written for an older workflow and has not been updated. Use the module path in your own go.mod instead.

Second, and more consequential, the README carries an explicit warning about the library's compatibility guarantee: if you want to use it in a production project, please use vendoring, because the author cannot ensure backward compatibility before release v1.0. That advice is unusual and worth honoring. Pinning the dependency into your own tree means a breaking change upstream cannot reach you at an inconvenient moment.

The project has never reached v1.0. The tags run v0.1.5 in 2019, v0.1.6 in 2022, and v0.1.7 in July 2026.

On platform scope, the README states plainly that only UNIX-based systems are supported and Windows is not, and adds that the library was tested only on Linux and macOS, inviting reports from anyone who can test elsewhere.

## What v0.1.7 fixed, and what that implies about trusting it

The most recent release is the most informative document in the repository, because it names the specific failure modes that were found.

The v0.1.7 changelog records a fix for a race condition on writing in the write pipe, with the broken pipe error named directly, and adds a feature for setting child stderr and stdout by file descriptor. Those are two real-world daemon problems: output redirection that can fail under concurrency, and the inability to control where a daemon's output goes, which is what you need for log collection.

The v0.1.6 release from 2022 added riscv64 support on Linux, removed the obsolete darwin/386 pair, and made pid file release failures return an error rather than being swallowed. That last change matters, and it connects directly to the deferred Release in the example: a pid file left behind by a crash makes the daemon look like it is already running, which blocks restarts.

That is a fair summary of the library's character. It is small, it is maintained, and its bug history is made of the exact edge cases that daemonization produces. The repository is not archived and the last push recorded is 2026-07-19, matching the v0.1.7 release. With 255 forks and 23 open issues, it is widely depended on, which is a reasonable signal about how much other people rely on those details being right.

## Conclusion

go-daemon solves one specific problem cleanly: getting a Go process into the background while keeping the runtime you already built. Its trick, re-executing the binary with a marker variable and branching on whether that variable is set, is the correct answer to the fact that fork cannot carry goroutines across the boundary, and the three-line Pre and Post structure makes the model easy to adopt. The cost is that you are restructuring your main around two entry paths, and the version history is telling enough to read before you commit. The newest tag, v0.1.7, arrived in July 2026 with a race condition fix and support for setting child stdout and stderr by file descriptor, so the project is maintained, yet it has never left pre-1.0 and the author explicitly recommends vendoring. Follow that advice if this goes into something you need to keep running.

## FAQ

### What is a daemon used for?

A daemon is a background process that runs without an attached terminal, starts independently of a login session, and is controlled by signals rather than by a user pressing keys. On a server that is how long-running services such as web servers, indexers and log processors are run, so they survive a logout and can be restarted without finding the terminal they were started from.

### How does go-daemon avoid the fork problem in Go?

It does not fork. The library re-executes the program's own binary with a predefined environment variable set, and treats that variable's presence as the signal that it is running as the child copy. Since fork in a Go runtime would produce a child that does not inherit the parent's threads and goroutines, re-exec preserves a complete runtime in the new process.

### Does go-daemon work on Windows?

No. The README states that only UNIX-based systems are supported and that Windows is not. It goes further and notes the library was tested only on Linux and macOS, asking anyone able to test other platforms to report back. The codebase carries separate Unix implementations and stubs, with an additional lock file variant for Solaris.

### Is go-daemon stable enough for production use?

The project has never released v1.0, and the README explicitly advises using vendoring in production because the author cannot guarantee backward compatibility before v1.0. It is actively maintained: v0.1.7 shipped in July 2026 fixing a broken pipe race and adding control of child stdout and stderr by file descriptor. Vendor it and pin the version.

## Sources

- [Issues](https://github.com/sevlyar/go-daemon/issues)
- [License: MIT](https://github.com/sevlyar/go-daemon/blob/master/LICENSE)
- [README](https://github.com/sevlyar/go-daemon/blob/master/README.md)
- [Releases](https://github.com/sevlyar/go-daemon/releases)
- [sevlyar/go-daemon on GitHub](https://github.com/sevlyar/go-daemon)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/sevlyar-go-daemon
