SystemD startup file for Clojure deployment

Due to the deprecation of Immutant I can’t easily build Wildfly-compatible war files. I’m abandoning use of Wildfly and plan to begin deploying jars that are run with java -jar. I’m not an expert yet at writing .service files. Does anyone have any examples I could draw on for systemd files to manage system start, stop, and restart of these applications?

Description=The Server

ExecStart=/usr/bin/java -Xms512m -Xmx512m -server -cp /path/to/the-server.jar clojure.main -m the-server.server 8090


Always set the heap size with -Xms and -Xmx, and they should be equal. The StartLimitInterval, Restart, and RestartSec options are explained in this article. They make sure the program restarts if it crashes or gets killed.

Remember to always run systemctl daemon-reload when you add or change a service file. Then just administrate via systemctl (start|stop|restart) the-server.

It might be worth looking into docs for the Type option as well.


Something interesting to dig deeper is the Type=notify option. If you have an APP/API with some /health-check endpoint being monitored by other services like Nagios you can communicate through sd_notify() with the system process of your APP and restart or stop the service.


That notify option sound fascinating. I want to learn more!

You’ll find lots of great examples of Systemd service files on your computer. Three tips: (1) read the systemd manual, (2) think upfront about the Type, because it is crucial to deciding how you’ll make your program start and stop, (3) don’t feel boxed-in to invoking the “java -jar…” right there in the service file. You’ll have more flexibility if the service unit designates shell scripts to start and stop your program.

Might want to add


to the [Service] section.

1 Like

Is there a reason you use a full java reference in the ExecStart rather than a java -jar -myapp.jar? When I build a thing with lein uberjar that how I usually take the jar for a spin. I’m also a Leiningen user so include the Xms and Xmx directives within the actual project.clj, and leave out the -server, -cp, -m, and clojure.main references. Just curious, as you’re clearly more comfortable with serving Java servers than am I.

It’s been a while, but I think the reason was that I don’t need a manifest in the jar, so there’s one less thing to go wrong. I think it was suggested by @seancorfield somewhere.

It’s also very convenient for me to copy and paste the line and change parameters, like just leaving off the end to start a REPL.

Even with a lein uberjar you can start it with java -cp ... clojure.main -m my.main.ns and that means, as John notes, you can also start a REPL with that same code environment, or run a different -main function etc.

The AOT compilation (and (:gen-class) in your main namespace) and manifest all combine to allow java -jar ... and have it default to running your -main function directly.

For a long time we ran our production processes via java -cp ... clojure.main -m my.main.ns but we switched to AOT / (:gen-class) / manifest for our primary processes purely for the start up speed improvement. Once your code is up and running, “all things are equal” as far as performance etc is concerned.

1 Like

Leiningen JVM options, including Xms and Xmx, affect the JVMs that Leiningen starts, but aren’t they lost when Leiningen makes an uberjar? I think you must still specify Xmx and Xms when invoking an uberjar, even if it was made by Leiningen and Leiningen knew how much memory you wanted in your development environment.

Correct. Having those options in project.clj only affects lein run etc. Once you’ve produced an uberjar, you’re in plain ol’ Java land and when you use java to start up a process, you’ll need to provide any JVM options you need for running that process.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.