Running a webserver
Not a public one, a local one for testing and development.
These days, lots of web development really requires a web server. The browsers are unwilling to serve up scriptable content off the filesystem, or let web applications access it. Precisely what the constraints are depends on the browser and the operating system, but the bottom line is,On some browsers, on some operating systems, you can disable some of the security features that are preventing scripts from accessing your filesystem. For the love of all things, don’t do that. if you’re developing an application that does any scripting, you want to run a local web server.
For a couple of decades, I ran an Apache webserver as a system service. I configured lots of virtual hosts on it with different ports. So “localhost:8133” would access a local version of “nwalsh.com”, “localhost:8134” would access a local version of “xproc.org”, etc. It worked fine, but by the time there were a couple of dozen ports, it was a little hard to remember them. “Is the local XProc test suite on 8137 or 8138?”
More recently, for projects like the XProc test suite, I have a gradle task that starts a web server in a Docker container for that project. (On more complicated projects, like this weblog, it’s a networked collection of containers.) Because these servers don’t have to run all the time, I can mostly use the same ports for all of them. That would be inconvenient if I needed to work on two of them at the same time, but that hasn’t happened yet.
For really simple cases like, “if I commit this fix, will the index page for qt4cg.org look ok?”, a gradle task is overkill. There isn’t even a build script in the branch that serves that content, and I don’t want there to be.
What I realized a while back, was that I could magically incant a temporary webserver into existance. To wit:
docker run -it --rm -p 8125:80 \
-v`pwd`:/usr/local/apache2/htdocs \
httpd:2.4
What that says is, start a Docker container running the httpd:2.4
container (an Apache webserver). Serve up the current working
directory on port 8125, and automatically discard the container when
the process ends. It runs in the foreground, printing the server log
messages while it runs. (Exactly how it says that, I leave as an
exercise to the reader.)
So, for a bunch of months, every time I’ve wanted an ad hoc web server, I’ve searched back through my shell history, found one of those, tweaked it as necessary and started it. That’s been fine, as far as it goes. (And if that trick is all you take away from this post, “you’re welcome.”) But it’s a little bit annoying. And what do programmers do when something is annoying?
We script it. Enter a little python script, webserver.
What webserver
does is provide a slightly less opaque set of options
to run an Apache webserver in a Docker container.
Run
webserver
and it’ll start a container in the background that serves the current working directory up on port 8125. Run
webserver --port 8130 --root ~/Projects/mysite.org/build/www
and it’ll start a container in the background that serves
~/Projects/mysite.org/build/www
on port 8130.
I decided that clogging up a shell window with the log wasn’t
something I actually cared about very often, so I’ve made the default
behavior to just leave it quietly running in the background. If you
start with the --foreground
option, it’ll run in the foreground
showing you the log. (Note, however, that hitting Ctrl-C to get your
terminal back won’t stop the server.)
To see what servers are running, use the --list
option:
webserver --list
That’ll print a list like this one:
Serving /Users/ndw/Projects/mysite.org/build/www on port 8130 with gifted_nightingale
Serving /Volumes/Projects/python/webserver on port 8125 with magical_ramanujan
Web servers are 2 of 6 containers
The server names are randomly generated by Docker unless you specify
the --start
option explicitly:
webserver --start ixml --root /Volumes/Projects/nineml/ixml
If you specify a name, that’ll be used. Now the list shows:
Serving /Volumes/Projects/nineml/ixml on port 8126 with ixml
Serving /Users/ndw/Projects/mysite.org/build/www on port 8130 with gifted_nightingale
Serving /Volumes/Projects/python/webserver on port 8125 with magical_ramanujan
Web servers are 3 of 7 containers
Note that because I didn’t specify a port, webserver
picked the “next available” port.
Also note that the list option only shows you webservers. I have four other Docker containers
running, but those aren’t shown.
(FYI: If you specify a name or port that’s in use by webserver
, you’ll get a friendly message
about it. If you specify a name in use by some other Docker container or a port used by
some other process, you’ll get a much less friendly error message.)
Generally, the names are irrelevant, but you can use them to stop a specific container:
webserver --stop gifted_nightingale
or to show the logs from a container that’s running in the background
webserver --logs ixml
(You can combine the --logs
and --foreground
options to watch the
logs for any running container.)
If you want to just stop all the containers --stop-all
is your friend.
And --help
will summarize all the options.
Share and enjoy!
P.S. If you want to run some other container, such as Nginx, or if you want the default port to be something other than 8125, you’ll have to edit the script. There are some class variables around line 16 that you can change. It didn’t seem like it was worth the effort to have options for those, or to read them from some sort of configuration file, but let me know if you disagree. Or, you know, submit a pull request!