crystal-accomplice
Accomplice
Allows any Crystal language program to easily run as a Windows Service with minimal change, while continuing to support running as a console application also.
Handles the Windows Service API for you. Adding Windows Service support is as simple as adding the dependency on Accomplice to your shard.yml file, and including a require "accomplice" line in your program before it does any meaningful work.
Should you wish to add support for graceful shutdown, doing so requires minimal additional effort, either:
- Add
at_exithandlers to perform graceful shutdown tasks, or - Use
Process.on_terminateto register your own interrupt signal handler to perform graceful shutdown tasks and thenexit.
By default Accomplice waits 5 seconds for your program to gracefully shutdown, after which it forcibly stops the process. This timeout can be changed if required as follow:
# Change the shutdown timeout from the default 5 seconds
# to 30 seconds
Accomplice::Windows::Service.shutdown_timeout = 30.seconds
You can also continue to run your program manually via the console after the above changes, without interference from Accomplice or the Windows Service API.
Note: a Windows Service necessarily requires at least two (2) threads, and this is accomplished using Crystal execution contexts as follows:
- the service dispatcher thread, started in an isolated execution context, and
- at least one other thread to run the actual service logic, the main thread.
Usage
-
Add the dependency to your
shard.yml:dependencies: accomplice: github: lachlan/crystal-accomplice -
Run
shards install -
Require
accomplicein your program, and then optionally include logic to gracefully shutdown your program when interrupted:
# Accomplice handles the Windows Service API for you, so that your
# program can be started and stopped by the Windows Service Control
# Manager (SCM).
#
# When you `require "accomplice"`, if the program was started by SCM
# then Accomplice will register the program as a Windows Service with
# SCM and set the status of the service to running.
#
# Otherwise if the program was started normally as a console
# application, Accomplice has no effect and your program runs normally
# as it always has before.
require "accomplice"
# Accomplice converts Windows Service Control Manager stop/shutdown
# controls to CTRL+C (SIGINT) interrupt signals to request the program
# to stop.
#
# Accomplice also includes a default interrupt signal handler which
# calls `exit`, allowing `at_exit` handlers to be used to gracefully
# shutdown:
at_exit {
# ...perform graceful shutdown tasks...
}
# OR you can replace the default interrupt signal handler with your
# own by using `Process.on_terminate` with a handler that stops your
# program gracefully and then exits:
Process.on_terminate do |reason|
Log.info { "SHUTDOWN INITIATED BY PROCESS TERMINATION, REASON = #{reason}" }
# ...perform graceful shutdown tasks...
exit
end
# Then run your program's logic as per normal:
loop do
# ...do stuff...
sleep 1.second
end
-
Compile:
shards build -
Create Windows service (note: these commands need to be run as an Administrator):
sc create <ServiceName> binpath= <ExecutablePath>
sc config <ServiceName> start= <boot|system|auto|demand|disabled|delayed-auto>
sc config <ServiceName> DisplayName= "<Service Display Name>"
sc description <ServiceName> "<Service Description>"
- Run Windows service
sc start <ServiceName>
...
sc stop <ServiceName>
Contributing
- Fork it (https://github.com/lachlan/crystal-accomplice/fork)
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
Contributors
- Lachlan Dowding - creator and maintainer
crystal-accomplice
- 1
- 0
- 0
- 0
- 0
- 7 days ago
- April 29, 2026
MIT License
Mon, 07 Sep 2026 00:55:30 GMT