Introduction
After spending a bit of time twidling around creating the simpliest of REST API using some
racket lang, it was time to find a way to deploy it, and make it accessible from my
domain. I spoke a bit about subdomains in
apache , and I'll apply it in this
case because I want to have an api
subdomain for all backend services I'm offering on
this domain. This racket specific one should be accessible from api.platyshell.me/facts
.
TL;DR: In order to manage a racket process from systemctl, we just need to have a
unit.service
file that contains a path to the racket binary, and eventually some arguments that it should use. We then use Apache as a reverse proxy in order to expose its boundaries.
Create a systemd unit file
I want the unit file to be controlled by the racket user I have created. After browsing
through the best way to go about this, I found a working solution in the wonderful arch
wiki to be the one that matches the most what I am trying to do1 . The unit file is
written in the /home/racket/.config/systemd/user/<unit>.service
and a minimal content
might look like this:
[Unit]
Description= Racket facts %i
[Service]
# racket project is located at /srv/racket/facts
WorkingDirectory=/srv/racket/facts/
ExecStart=/usr/bin/racket /srv/racket/facts/app.rkt %i
[Install]
WantedBy=multi-user.target
Depending on weither or not you want the racket process to access ports 80 and 443, you
might want to add an entry AmbientCapabilities=CAP_NET_BIND_SERVICE
to the [Service]
header that should
enable it to2.
In our case we don't need to as we'll use Apache to serve the racket
internal port to the outside world through a reverse proxy.
You can then give it a spin by doing a systemctl --user start facts.service
. I'm running this on a debian box, and I run in a couple of issues
regarding the a failed connection the the bus, but found a working solution here.
Edit: Ultimately, the issue came from the fact that the user's runtime switch isn't handled when changing user through the
su
command on an SSH connection. That means that there are no such issue when ssh-ing directly as the user handling the service.
Setup the subdomain and proxy requests through Apache
Setup the subdomain
To register the api
subdomain, all I had to do was create the following apache
configuration file in /etc/apache2/sites-available/api.subdoma.in.conf
.
<VirtualHost api.doma.in:80>
ServerAdmin mymail@doma.in
ServerName api.domain.in
Redirect / https://api.doma.in
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
<VirtualHost api.doma.in:443>
ServerName api.doma.in
SSLEngine on
SSLCertificateFile /etc/ssl/certs/doma_in.pem
SSLCertificateKeyFile /etc/ssl/private/doma_in.key
# Racket runs on the 8000 local port by default when using their web-server
# module. This can obviously be changed accordingly
ProxyPass / http://localhost:8000
ProxyPassReverse / http://localhost:8000
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
In this example, I'm also configuring a redirection from HTTP to HTTPS, but it isn't
necessary. The actual important lines are the ProxyPass
and ProxyPassReverse
ones.
A simple a2ensite api.subdoma.in.conf && systemctl reload apache
should bring the whole thing up.
Conclusion
Once everything is brought up, you should be able to reach the subdomain, and get the expected output from the racket process. One obvious thought I had writing and setting this up was how could this be automated. I'll show how I ended up bundling my microscopic racket app to be deployed and used automatically in the next post. I wrote this small article as I couldn't find something wrote up for this specific purpose, in hope that it helps someone.
Footnotes
1 : You can also create a unit file in /etc/systemd/system/
and add a couple of
entries to the [Service]
header like so:
[Unit]
Description= Racket facts %i
[Service]
User=racket
Group=racket
# racket project is located at /srv/racket/facts
WorkingDirectory=/srv/racket/facts/
ExecStart=/usr/bin/racket /srv/racket/facts/app.rkt %i
[Install]
WantedBy=multi-user.target
Don't use those extra lines if you're planning on having the unit file in your local
configuration file (~/.config/systemd/user/unit.service=
) as it will throw out a obscure
error about how it "fails to determine supplementary groups". Check here if you stumble
upon this issue.
2 : Details found in a "racket users" google groups conversation here.