Start and stop a local server with make

When you work on a web app or other web related project you probably need a local HTTP server for testing during development. One way for comfortably starting and stopping a server (or any other service running in the background) is using make.

Make runs commands to create and/or transform files and uses the files' timestamps to determine if a command needs to be executed or can be skipped. For example:

foo:
	$(info Creating foo ...)
	touch foo

When you save the above in a file called Makefile and run make foo it will output "Creating foo ..." and create an empty file foo. When you call make foo again make detects that foo already exists and simply prints "'foo' is up to date" without calling touch again.

Starting the server

We can use this behavior to start a server and store its process ID (PID) in a file:

server.pid:
	http-server . & echo $$! > $@

Let's break this down. http-server is a simple web server that can be installed via npm (npm install -g http-server). But you can use any other server implementation you like. The "&" sends the server process into the background (i.e. it doesn't lock the console). "echo $$! > $@" writes the PID ($$!) to the file server.pid ($@). Calling make server.pid will start the server and create server.pid just like the touch example above. It will also not start the server again if server.pid is already present.

We can now create a make target that allows us to start our server with the command make start-server:

start-server: server.pid

This simply means "The target 'start-server' depends on the file server.pid. So make sure it exists to complete this target."

Stopping the server

Stopping the server is a bit more envolved:

stop-server:
	test -f server.pid && kill `cat server.pid` && rm server.pid || true

"test -f" tests if server.pid is present and stops if it's not. "kill" ends the process with the ID that's stored in server.pid and "rm" deletes the file. The last bit "|| true" ensures that make returns without an error if server.pid does not exist.

The entire Makefile looks like this:

.PHONY: start-server stop-server

start-server: server.pid

stop-server:
	test -f server.pid && kill `cat server.pid` && rm server.pid || true

server.pid:
	http-server . & echo $$! > $@

The .PHONY bit tells make that start-server and stop-server are targets that (unlike server.pid) do not correspond to files with those names.

Now you can start and stop your server like so:

$ make start-server
$ make stop-server

There is one caveat with this approach. In case you forget to make stop-server before shutting down your machine you end up with a stale server.pid file that you have to delete manually before you can start the server again.

Happy serving! ;-)