Caddy: feature: support running under windows service

Created on 28 Oct 2015  路  20Comments  路  Source: caddyserver/caddy

Would you be open to a PR that under windows on load
a. detects if running as a windows service
b. if so, uses the windows service start, stop callbacks that enable running under windows services.

Could use either:
https://godoc.org/golang.org/x/sys/windows/svc
or https://godoc.org/github.com/kardianos/service

feature request plugin

Most helpful comment

yes, you could add a plugin that does one thing, import github.com/kardianos/minwinsvc and that's all. On non-windows compiles it would do nothing. On windows compiles it would allow registering as a windows service.

Or if you want you could import minwinsvc directly. It would do nothing for non-windows boxes, yet make it an option to run caddy as a windows service directly in windows. Either would work.

Neither would make a UI within caddy for easily setting up the service. Which is fine.

All 20 comments

The idea sounds interesting. Could you please explain a little bit more how do you propose to register the service. From what I understood a) and b) rely on an already installed service to detect and manage it.

The final goal of this would be to enable running caddy directly as a windows service which requires some callbacks and a control loop. Secondary items to address are making it easy to install and auto-sense when it is being run as a service or in cmd.

The kardianos/service package wraps the x/sys/windows/svc package on windows and exposes a uniform API for all platforms. A decision on if this change should be windows specific or not would need to be made.

An example of the service in action may be found here: https://github.com/kardianos/service/blob/master/example/logging/main.go
The following code is an excerpt from the above link and contains the code that installs, starts, stops, restarts, and removes the services:

    if len(*svcFlag) != 0 {
        err := service.Control(s, *svcFlag)
        if err != nil {
            log.Printf("Valid actions: %q\n", service.ControlAction)
            log.Fatal(err)
        }
        return
    }
    err = s.Run()

If there isn't a control flag, then it simply runs the service. The Run() method does a detection if it is running under a service manager or not which determines how it runs the executable (on windows, if it should use the service callbacks or now).

Even if you don't include or use the code to control the service, windows ships with a command called "sc" users could use to install, start, stop remove services, though most windows users who aren't sysadmins know of it.

You may wish to integrate a control flag to enforce the user to provide a Caddyfile location as the CWD is not settable for windows services.

The delta for this pull request that used the kardianos/service package would be:

  • Add a flag called something like "-instance-name", optional to identify different caddy service instances. (this flag could be omitted).
  • Add a flag called something like "-service", used to install, start, restart, stop, uninstall services.
  • In the main, probably replace app.Wg.Wait() with serviceInstance.Run() (still wait in a separate goroutine and signal stop when done). Care would need to be taken to not get in the way of something like graceful restarts or similar, if that is a goal.
  • In the main func, define the service. The service name could be "Caddy{{if instance name}}${{.InstanceName}}{{end}}". And populate the arguments for the Caddyfile location when installing the service.

I do like the idea of "caddy as a service" -- but hold out on any implementation details, since the letsencrypt branch is going to land in master soon, which completely refactors the entire core -- making it very easy to start, stop, and restart the listeners, making it very service-friendly.

So I like where this is going. Keep an eye on master to see when letsencrypt gets merged into it. After the next release, would be a good time to start working on this.

@mholt Sounds good.

@kardianos The merge is completed, and I don't anticipate any huge changes for the next little while, if you wanted to start looking into the service feature.

@mholt Would you prefer this in the form of windows only support that enables manually installing and running as a web service, or would you like to see two additional flags that allow cross platform installing and controlling as a service?

I just saw your repo at https://github.com/kardianos/minwinsvc - I haven't given it a once-over yet and I still need to figure out the answers to your questions. :smile: But we'll get around to it!

In the mean time,
if anyone is looking how to get Caddy setup as windows service. I recommend NSSM. It wraps CLI applications and manages their execution.

Hey @kardianos and others in this issue - thanks for your comments! There is a lot of good stuff here, so I have linked to this issue from a thread in our community forum where we are aggregating ideas/requests for Caddy plugins. It's quite possible, with Caddy 0.9, that maybe this kind of feature could be implemented as a plugin. I remember your pull request #588, kardianos, and it's totally possible, I think, to make it a plugin. And it would be as easy as your PR.

If you want to continue discussing this, feel free to start a new topic on the forum or re-open the issue. :+1: Again, not closing because I don't want this, just trying to get through my backlog, since this'll probably be a better plugin than built into Caddy core.

yes, you could add a plugin that does one thing, import github.com/kardianos/minwinsvc and that's all. On non-windows compiles it would do nothing. On windows compiles it would allow registering as a windows service.

Or if you want you could import minwinsvc directly. It would do nothing for non-windows boxes, yet make it an option to run caddy as a windows service directly in windows. Either would work.

Neither would make a UI within caddy for easily setting up the service. Which is fine.

What would a plugin look like here? It would call RegisterPlugin() of course, but how would it get access to the *Instance to call ShutdownCallbacks() and Stop()?

I don't see where plugins have access to initiate a shutdown (as opposed to just reacting to a shutdown).

Registering/unregistering the service should only be triggered once, hence through a command line argument rather than a Caddyfile directive. Can plugins acces the args and prevent the server from starting if caddy.exe is only used as a cli to register/unregister the service?

Maybe it would be better to have a separate caddy_cli.exe than a caddy plugin? (Like e.g. postgres)

@Echsecutor this issue is limited in scope to enabling a compiled caddy.exe to be _used_ as a service, and _explicitly not_ about the process of registering it as a service. To do that you would use an external program, for example, sc create among others.

Plugins can register their own command line flags, yes.

@mholt So how would a plugin that decided that caddy should shutdown communicate that up to caddy? E.g. how would it call executeShutdownCallbacks()? Windows service messages are basically a different kind of signal and handling them should hook into the standard shutdown system.

Still working on those details...

@mholt any progress here on the plugin api?

@infogulch Yes, @hacdias will have some news regarding this on April 20, or after he gets back from his trip.

Awesome. Eagerly awaiting the live event! I'll certainly try to join via livestream. :)

FWIW, @hacdias helped work on this plugin which is now available: https://caddyserver.com/docs/hook.service

Was this page helpful?
0 / 5 - 0 ratings