windows-service-rs, the Rust crate Mullvad uses to run its VPN as a Windows service
Windows services in Rust
At a glance
- What is it?
- windows-service is a Rust crate that handles the parts of writing a Windows service that are tedious rather than hard: the FFI entry point, the control event handler and the status transitions the service manager expects. It suits anyone shipping a background daemon on Windows, and its examples cover install, uninstall, pause and failure actions that the README never explains.
- Who is it for?
- Adopt windows-service-rs if you are writing a Rust program that has to run as a Windows service under the service control manager, since the crate removes the FFI boilerplate and the status state machine, and it comes from a vendor that runs its own VPN client as a service. Do not adopt it for a cross-platform daemon where Linux systemd or macOS launchd would do, because the dependency set is Windows-only and the crate is not portable.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 58 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A macro that generates the FFI entry point
windows-service is a crate that provides facilities for management and implementation of Windows services, and its authors are Mullvad VPN.
The problem it addresses is the shape of the Windows service API. Each service has to implement a service entry function with the signature fn(argc: u32, argv: *mut *mut u16) and register it with the system from the application's main. That is raw FFI with an awkward argument convention, and every Rust service ends up writing the same wrapper.
The crate provides a define_windows_service! macro that generates the low level boilerplate for that entry function, parses the input from the system, and delegates handling to a user defined higher level function taking fn(arguments: Vec<OsString>). The README uses the names ffi_service_main for the low level entry and my_service_main for the higher level one, while noting it is up to the developer how to call them.
The result is that the part you cannot avoid is four lines, and the part you want to write is a function with a Rust signature:
define_windows_service!(ffi_service_main, my_service_main);
fn my_service_main(arguments: Vec<OsString>) {
// The entry point where execution will start on a background thread after a call to
// `service_dispatcher::start` from `main`.
}The dispatch happens in main, where service_dispatcher::start registers the generated entry with the system and blocks that thread until the service is stopped. The crate version in the manifest is 0.8.1, it needs Rust 1.71.0 or newer, and the docs.rs build targets x86_64-pc-windows-msvc.
The control handler is where a service earns its keep
The README is direct about the first thing a service should do: subscribe for service events such as stop or pause, and many others.
The handler is a closure that matches on a ServiceControl value and returns a ServiceControlHandlerResult. Two rules are visible in the example. The Stop case has to return control back to the system, and all services must accept Interrogate even if it is a no-op. Anything else returns NotImplemented, which tells the service manager the service does not handle that control.
let event_handler = move |control_event| -> ServiceControlHandlerResult {
match control_event {
ServiceControl::Stop => {
// Handle stop event and return control back to the system.
ServiceControlHandlerResult::NoError
}
// All services must accept Interrogate even if it's a no-op.
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
_ => ServiceControlHandlerResult::NotImplemented,
}
};The handler is registered with service_control_handler::register, which returns a status handle, and the README links the MSDN article describing how the event handler works internally, so the Windows semantics are documented rather than folklore.
The reason to care about Interrogate is worth stating plainly. The service control manager sends it as a liveness check, and a service that returns NotImplemented for it can be flagged as not responding and restarted by Windows. It costs one line to handle and it prevents a genuinely confusing failure.
Status transitions with a wait hint and a checkpoint
The third piece is the state machine, and the README spends the most space on it because this is where a naive implementation goes wrong.
When the system launches an application that implements a service, it is automatically put in the StartPending state. The application then has to complete initialisation, obtain a ServiceStatusHandle through service_control_handler::register, and transition to Running.
If initialisation is lengthy, the service must tell the system how long it needs, by sending the StartPending state with a time estimate in ServiceStatus::wait_hint, and incrementing ServiceStatus::checkpoint each time a step completes. The consequence is stated bluntly: the system will attempt to kill a service that is not able to transition to Running before the proposed wait_hint expires.
So this is not optional politeness. A service that does a long initialisation, such as opening a tunnel, without updating the wait hint and checkpoint, gets killed by Windows with no useful diagnostic.
The same concept applies when transitioning between other pending states and their target states, and the status handle is safe to clone and use from any thread, which matters because a service usually has worker threads and a control thread.
The ServiceStatus struct in the example carries six fields with rules attached: service_type should match the one from the system service registry, controls_accepted declares which controls are honoured while running, exit_code is only used to report an error when starting or stopping, and checkpoint and wait_hint must be zero except in pending states.
The examples cover the parts the README does not
The README documents the three concepts above and stops. Everything else you need is in the examples directory, and the file names describe the surface area of a real service: install_service.rs, uninstall_service.rs, notify_service.rs, pause_continue.rs, ping_service.rs, service_config.rs and service_failure_actions.rs.
That list is a good map of the Windows-specific work. Installing a service and uninstalling it are separate programs, which is the standard arrangement because the service binary has to be able to remove itself from the service control manager. service_config.rs covers the configuration written to the registry, and the README notes in the status example that the service_type should match the one from the system service registry, which is the link to that configuration.
service_failure_actions.rs is the one that most projects forget. A service can be told what to do when it crashes, and the failure actions are configured outside the service process, in the registry, through the same configuration surface. The pause_continue.rs example covers the control events the handler must then accept, and notify_service.rs shows the notification path.
The README points to the examples folder for the complete source of a working program, which is the right way to read this project. The documentation is a reference for the three hard parts, and the examples are the how-to.
Windows-only dependencies, and a deliberately narrow surface
The dependency structure makes the platform scope obvious. The manifest declares its dependencies under a cfg(windows) target, so nothing is pulled in when the crate is compiled for another platform.
Under that target are bitflags 2.3 and widestring 1, plus windows-sys 0.61 with a curated feature list. The features are the interesting part: Win32_Foundation, Win32_Storage_FileSystem, Win32_System_Power, Win32_System_RemoteDesktop, Win32_System_Services, Win32_System_SystemServices, Win32_System_Threading, Win32_System_WindowsProgramming and Win32_UI_WindowsAndMessaging.
Those feature names tell you what the crate actually touches. The service control manager interfaces, the power and system service notifications, the threading primitives needed for the background thread the service runs on, file system access, and the window messaging used for notifications. This is not a general purpose Windows API binding. It is the smallest set that a service needs, which is why the crate compiles quickly and why it does not drag a large API surface into your dependency tree.
The category in the manifest says the same: api-bindings, os::windows-apis. It is a wrapper over the raw API, not a framework layered on top of it. That is why the README teaches you the Windows semantics, including the wait hint rule, rather than hiding them behind a runtime you do not control.
The licence is MIT OR Apache-2.0, with both LICENSE-MIT and LICENSE-APACHE files at the root, and the project is a fork of the original windows-service crate, given the repository URL and the Mullvad author field.
Editorial conclusion
Adopt windows-service-rs if you are writing a Rust program that has to run as a Windows service under the service control manager, since the crate removes the FFI boilerplate and the status state machine, and it comes from a vendor that runs its own VPN client as a service. Do not adopt it for a cross-platform daemon where Linux systemd or macOS launchd would do, because the dependency set is Windows-only and the crate is not portable. Before you commit, read the examples folder rather than the README, since the install, uninstall, pause and continue and failure action examples are where the practical behaviour lives, check that the Windows service registration matches your own installer, and note the maintenance picture, with version 0.8.1 released on 2026-05-08 and the last push on 2026-08-26.
Frequently asked questions
What does the windows-service Rust crate do?
It provides facilities for implementing and managing Windows services. The define_windows_service! macro generates the low level service entry function that parses input from the system and delegates to a higher level function of your own with a Vec<OsString> signature.
Why does a Windows service need to handle the Interrogate control event?
The README states that all services must accept Interrogate even if it is a no-op. Returning NotImplemented for it tells the service control manager the control is unhandled, and a service that does not respond to that liveness check can be treated as unresponsive and restarted by Windows.
What happens if my Windows service does not report a wait hint?
When a service is launched it is put in the StartPending state automatically, and it must tell the system how long it needs by sending a wait hint and incrementing the checkpoint as steps complete. The README states the system will attempt to kill a service that cannot reach the Running state before the proposed wait hint expires.
Which Rust version and platforms does windows-service-rs support?
The manifest declares rust-version 1.71.0, edition 2021, and the docs.rs build targets x86_64-pc-windows-msvc. The dependencies are declared under a cfg(windows) target, so the crate is Windows specific.
What licence is windows-service-rs released under?
The manifest sets license to MIT OR Apache-2.0, and the repository contains both a LICENSE-MIT and a LICENSE-APACHE file. The README states the licence as MIT/Apache-2.0.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mullvad-windows-service-rs)Was this useful?