How Dewy Performs Graceful Restarts for Server Applications
Dewy performs graceful restarts for server applications by intercepting SIGUSR1 to trigger a self-sent SIGHUP, which leverages the server-starter library to atomically hand over listening sockets to a new process without dropping active connections.
The linyows/dewy repository implements a zero-downtime deployment mechanism that allows Go server applications to update without interrupting existing requests. Understanding how Dewy performs graceful restarts for server applications requires examining its Unix signal handling architecture and its integration with the github.com/lestrrat-go/server-starter library.
Understanding Dewy's Graceful Restart Architecture
Dewy’s graceful restart mechanism relies on a coordinated sequence of Unix signals and external library support to achieve zero-downtime deployments. The architecture separates the trigger signal (SIGUSR1) from the handoff signal (SIGHUP), allowing the system to distinguish between user-initiated restarts and internal process management.
When Dewy receives SIGUSR1, it initiates a self-signaling process that ultimately invokes the server-starter library. This library creates a new instance of the server binary while passing the existing listening socket file descriptors to the new process. The old process continues serving existing connections until the new process signals readiness, at which point the old instance terminates gracefully.
Implementation Details in the Dewy Source Code
The graceful restart logic is implemented across dewy.go and starter.go, with specific functions handling signal trapping, restart initiation, and server process management.
Signal Handling with waitSigs
In dewy.go (lines 193–210), the waitSigs function establishes a signal notification channel using signal.Notify. This channel listens for SIGHUP, SIGUSR1, SIGINT, SIGTERM, and SIGQUIT. When SIGUSR1 is received, the function invokes restartServer to begin the graceful restart sequence.
The implementation ensures that the signal handler runs in a dedicated goroutine, preventing blocking of the main application loop while waiting for system signals.
Initiating the Restart with restartServer
The restartServer function (lines 473–485 in dewy.go) handles the actual restart trigger. It acquires a mutex lock to prevent concurrent restarts, retrieves the current process ID using os.Getpid(), and sends SIGHUP to itself via os.FindProcess(pid).Signal(syscall.SIGHUP).
This self-signaling approach decouples the user trigger (SIGUSR1) from the restart protocol (SIGHUP), allowing the server-starter library to intercept the SIGHUP and manage the process handoff without confusing external signal sources with internal restart coordination.
Server Process Management with startServer
In dewy.go (lines 488–514), the startServer function initializes the graceful restart capability by creating a starter.Starter instance. It passes the user-provided StarterConfig to starter.NewStarter(), then invokes s.Run() in a background goroutine.
The github.com/lestrrat-go/server-starter library manages the SIGHUP signal by spawning a new server process, passing the inherited file descriptors for listening sockets, and coordinating the shutdown of the old process only after the new one is ready to accept connections.
Configuration Structure in starter.go
The StarterConfig type defined in starter.go (lines 10–57) specifies the parameters required for the graceful restart mechanism. Key fields include the command to execute, port numbers to bind, and signal names (sigonhup, sigonterm) that configure how the server-starter library responds to system signals.
This configuration is passed through from Dewy’s main configuration to the underlying library, allowing customization of the restart behavior while maintaining the zero-downtime guarantee.
Practical Usage: Triggering a Graceful Restart
To perform a graceful restart of a server application managed by Dewy, send the SIGUSR1 signal to the running Dewy process. This initiates the internal sequence that results in a zero-downtime handoff to a new process.
# Start Dewy (assumes it manages a web server)
$ dewy run
# Deploy a new artifact (Dewy downloads and updates the 'current' symlink)
# Trigger graceful restart
$ kill -SIGUSR1 $(cat /var/run/dewy.pid)
When SIGUSR1 is received, Dewy’s waitSigs function calls restartServer, which sends SIGHUP to itself. The server-starter library intercepts this signal, starts a new instance of the server binary, passes the listening socket file descriptors, and gracefully terminates the old process only after the new one is ready.
For automated deployments, configure your CI pipeline or systemd to send SIGUSR1 whenever a new release is detected, enabling fully automated zero-downtime updates.
Summary
- Signal decoupling: Dewy uses
SIGUSR1as the user-facing trigger andSIGHUPas the internal handoff signal to separate user commands from process management. - Server-starter integration: The
github.com/lestrrat-go/server-starterlibrary handles the complex logic of socket passing and process coordination, invoked viastarter.NewStarterindewy.go(lines 488–514). - Zero-downtime guarantee: The old process continues serving existing connections while the new process initializes, with the handoff occurring only after the new process signals readiness.
- Core implementation: Signal handling resides in
waitSigs(lines 193–210), restart initiation inrestartServer(lines 473–485), and configuration instarter.go(lines 10–57).
Frequently Asked Questions
What is the difference between SIGUSR1 and SIGHUP in Dewy's restart flow?
Dewy uses SIGUSR1 as the external trigger for users or automation systems to request a restart, while SIGHUP serves as the internal mechanism to execute the handoff. When SIGUSR1 is received by the waitSigs function in dewy.go, it calls restartServer, which sends SIGHUP to the same process. This separation allows the server-starter library to intercept SIGHUP specifically for socket-passing operations while keeping the user interface distinct.
How does Dewy ensure zero-downtime during server restarts?
Dewy achieves zero-downtime restarts by leveraging the github.com/lestrrat-go/server-starter library's socket-passing capability. When SIGHUP is triggered, the library starts a new server process and passes the existing listening socket file descriptors to it. The old process continues serving established connections until they complete, while the new process accepts new connections. The old instance terminates only after the new process signals readiness, ensuring no active requests are dropped during the transition.
Can Dewy perform graceful restarts without the server-starter library?
No, Dewy relies entirely on the github.com/lestrrat-go/server-starter library to handle the low-level details of graceful restarts. The startServer function in dewy.go (lines 488–514) initializes the library via starter.NewStarter and executes s.Run() to manage the process lifecycle. Without this library, Dewy would not have the socket-passing and process coordination mechanisms required for zero-downtime restarts.
Where is the graceful restart logic implemented in the Dewy source code?
The graceful restart logic is distributed across two main files in the repository. Signal handling and restart initiation reside in dewy.go, specifically in the waitSigs function (lines 193–210) for signal trapping and restartServer (lines 473–485) for triggering the handoff. The integration with the underlying restart mechanism occurs in startServer (lines 488–514), which configures the server-starter library. Configuration parameters for the restart behavior are defined in starter.go (lines 10–57) within the StarterConfig struct.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →