Getting started with a GNU Guix VPS
This tutorial will let you edit the starting configuration of a GNU Guix VPS from https://guix-hosting.com. You will be guided on how to make your VPS serve static content over HTTP, and then start a PostgreSQL instance. This guide expects some level of familiarity with the command line and a basic knowledge of networking, but is fairly accessible to beginners.
Advanced users will be able to adapt the procedure to their own goals.
Throughout this tutorial, we will link to the relevant sections of the GNU Guix Reference Manual. Its size can be daunting, but almost all the information you need is there. The little that is not there can be grasped by reading the source code.
Other community help resources exist such as the cookbook, the mailing lists, etc.
Establish a connection to the VPS
Soon after you subscribe, you will get an email with the IP of your VPS. For the
sake of this tutorial, we will assume that the domain name example.com
points
to this IP.
You can use the IP directly anywhere we use example.com
, or of course
substitute your own domain name.
You should now be able to log in to the VPS with
ssh root@example.com
provided you have loaded the private key corresponding to the public key you have given when you subscribed.
You will be greeted by the following prompt:
root@minimal-ovh ~#
You can close the shell (ctrl-D, or type exit
), the goal was only to make sure
you can connect to your VPS.
Get a copy of config.scm
The whole VPS is configured in one file, /etc/config.scm
. This is a scheme
file. Scheme is a programming language (see the crash course), which will come
very handy once your configuration will become even moderately complex. The
ability to store variables, loop over stuff, fetch information from other files,
define your own operators, etc. is a huge boost over a simple markup language
such as JSON or YAML.
For this tutorial, however, we will stick with simple operations, you don’t need to know scheme.
First, grab a copy of this file by running:
scp root@example.com:/etc/config.scm ./
This will copy the config.scm
file into the current directory. I usually use
git
, a version control system, to keep a history of this file, and I think you
should too.
When you open this file, it should look something like this:
(use-modules (gnu) (beaver system)) (-> (minimal-ovh "ssh-rsa AAASomethingSomething== root@minimal-ovh") ;; Your config goes here )
The first two lines are akin to an import
statement in Python, or a #include
in C. The first argument to use-modules
will import core operators (use-package-modules
,
etc.) that are needed under the hood. The second one imports some utilities
written by Beaver Labs, the company behind https://guix-hosting.com, to make
your life easier1.
Forget about the ->
for a while (we will see what it means in the next
section). One of Beaver Labs’ utilities is minimal-ovh
. It is a function of
one argument. The form:
(minimal-ovh "ssh-rsa AAASomethingSomething== root@minimal-ovh")
is scheme’s way of calling the function on what you will recognize as the SSH public key you gave when you subscribed to your VPS.
In an Algol-like syntax (such as Perl’s, Python’s, C’s, etc.), it would have been written as:
minimal_ovh("ssh-rsa AAASomethingSomething== root@minimal-ovh")
This function returns an operating system definition that GNU Guix understands, and which contains quite a lean configuration with everything needed to run GNU Guix on an OVH VPS (as of
Beaver Labs’ underlying hosting provider is indeed OVH).We will now see how to build on this lean configuration to make our VPS do something.
Serve static content through HTTP
Another of Beaver Labs’ utilities is the http-static-content
function. It
takes two mandatory arguments:
- an operating system
- a domain name.
It will return an operating system that serves the contents of /var/www/
when
queried on port 80 via the given domain name.
So, for example, if you want to serve static content, you’ll change your
/etc/config.scm
to something that would be the same as the Algol-like:
http_static_content( minimal_ovh("ssh-rsa AAASomethingSomething== root@minimal-ovh"), from_host="example.com")
Most of the utilities defined in the (beaver system)
namespace work like this:
the first argument is an operating system, and the return value is an operating
system.
People familiar with functional programming will have seen this coming one section ago: one can chain such functions to add functionalities to the lean operating system we started with, until we are satisfied.
Indeed, if we wanted two subdomains, serving the contents of different
subdirectories in /srv/
, we would do something like:
http_static_content( http_static_content( minimal_ovh("ssh-rsa AAASomethingSomething== root@minimal-ovh"), from_host="sub1.example.com", to_dir="/srv/sub1/"), from_host="sub2.example.com", to_dir="/srv/sub2")
But this is becoming a little bit hard to read. This is because Algol-like syntax, unlike scheme’s, is irregular and set in stone.
Scheme’s regular syntax alone would make matters only slightly better:
(http_static_content (http_static_content (minimal_ovh "ssh-rsa AAASomethingSomething== root@minimal-ovh") #:from-host "sub1.example.com" #:to-dir "/srv/sub1/") #:from-host "sub2.example.com", #:to-dir "/srv/sub2")
Where scheme shines is with its homoiconicity, which allows for a powerful yet
simple macro system. The ->
macro2 allows us to chain functions seamlessly:
- the first form after
->
is the initial value, - every other form is a function call,
- the return value from the previous function call will be placed as the first argument of the next function call.
Long story short, the full /etc/config.scm
becomes, thanks to ->
:
(use-modules (gnu) (beaver system)) (-> (minimal-ovh "ssh-rsa AAASomethingSomething== root@minimal-ovh") (http-static-content #:from-host "sub2.example.com" #:to-dir "/srv/sub2") (http-static-content #:from-host "sub1.example.com" #:to-dir "/srv/sub1/"))
And we are back to the simple and intuitive syntax one could expect from an Ansible playbook or a Dockerfile, but in a much more organic way, and with complete control over the underlying abstraction.
To activate this configuration, upload the file to the server, and reconfigure your system:
scp config.scm root@example.com:/etc/config.scm ssh root@example.com guix system reconfigure /etc/config.scm
If you are unhappy with the changes, no worries, you can always get back to where you were before with a simple call to:
guix system switch-generation -- -1
As of utility functions:
, Beaver Labs’ channel provides the followingopenssh-root-key
,packages
,http-reverse-proxy
,https-reverse-proxy
,http-static-content
,https-static-content
,mumble
,users
,groups
,os/mkdir-p
os/file
nobody-like-user
,ssh-user
.os/setuid
os/setcap
suc-private-channel
suc-public-channel
suc-dropbox-channel
os/git
os/hostname
os/permaudit
os/9mount
os/mount.9p
join-the-tilde-club
os/listen
Using GNU Guix’ extensible service system
Those utility functions cover only a fraction of the 266 services you can manage with GNU Guix. In order to let you use a service that is not covered by a utility function, Beaver Labs’ channel also provide syntactic sugar to let you instantiate of extend any of GNU Guix’s service.
The manual gives a thorough explanation of how services work and can be used. We will show you here the quick way of adding a service to your VPS, but as you go on with GNU Guix, we strongly suggest you dive in, as the service system is really quite nice and clever.
Anyway, let’s assume for example you want to run an instance of the PostgreSQL database.
You can Ctrl-F your way through the manual, or you can also invoke:
guix system search postg
which will show you that there is a PostgreSQL service available:
name: postgresql location: gnu/services/databases.scm:320:2 extends: shepherd-root activate account profile shepherdnames: postgres description: Run the PostgreSQL database server. relevance: 5
Using Beaver Labs’ syntactic sugar, just add the following line to your VPS configuration:
(add-service postgresql)
Or maybe you want to change where the log will be stored, of which exact version of PostgreSQL is used.
You can see in the manual that postgresql-configuration
has a log-directory
and a postgresql
fields that can be set.
The line service then becomes:
(add-service postgresql
(postgresql postgresql-10)
(log-directory "/var/log/db")))
Now, you just need to
- add
(beaver functional-services)
to your imports at the top of the/etc/config.scm
file - and add the service to the operating system.
You also need to tell GNU Guix where to find postgresql-10
which is the
package you wish to use (this lets you specify the version you want).
In order to know where it is, run:
guix search postgresql
And you will find it:
name: postgresql version: 10.23 outputs: out: everything systems: x86_64-linux i686-linux dependencies: docbook-sgml@4.2 docbook2x@0.8.8 libxml2@2.9.12 opensp@1.5.2 openssl@1.1.1l perl@5.34.0 readline@8.1.1 texinfo@6.7 util-linux@2.37.2 zlib@1.2.11 location: gnu/packages/databases.scm:1330:2 homepage: https://www.postgresql.org/ license: X11-style synopsis: Powerful object-relational database system description: PostgreSQL is a powerful object-relational database system. It is fully ACID compliant, has full support for foreign keys, joins, views, triggers, and stored procedures (in multiple languages). It includes most SQL:2008 data types, including INTEGER, NUMERIC, BOOLEAN, CHAR, VARCHAR, DATE, INTERVAL, and TIMESTAMP. It also supports storage of binary large objects, including pictures, sounds, or video. relevance: 32
The location field tells you what to import:
(use-modules ... (gnu packages databases) ... )
The final /etc/config.scm
is now:
(use-modules (gnu) (beaver system) (beaver functional-services) (gnu packages databases)) (-> (minimal-ovh "ssh-rsa AAASomethingSomething== root@minimal-ovh") (http-static-content #:from-host "sub2.example.com" #:to-dir "/srv/sub2") (http-static-content #:from-host "sub1.example.com" #:to-dir "/srv/sub1/") (add-service postgresql (postgresql postgresql-10) (log-directory "/var/log/db")))
Don’t forget to enforce the changes by running
scp config.scm root@example.com:/etc/config.scm ssh root@example.com guix system reconfigure /etc/config.scm
Next steps
You can now browse the list of services ready to use with GNU Guix and add any of those to your VPS.
Keep your config.scm
file under version control, and you will be able to
reproduce the same configuration on any guix machine, and even generate VMs and
containers with the same configuration.
If you want to keep exactly the same version of every piece of software, down to a bit-by-bit exact replica, don’t forget to put the output of:
guix describe --format=channels
under version control as well. This plus your config.scm
will be more or less
what a pip freeze
or npm’s package-lock.json
does. But for your whole system.
The next step will be to learn how to package your own software and run it as a service, with the option to isolate it in a container for better security.
Changelog
- Changed the explanation of the syntactic sugar to use the new syntax
.foo
syntactic sugar for services
Add a brief explanation of the - Initial version
Footnotes:
This means that Beaver Labs’ channel has been added to the default configuration. Adding channels willy-nilly is a security risk, but we are your hosting provider, and anything we could do via a channel, we could do more discreetly by other means, you have to trust us somewhat (as you technically need to trust any hosting provider).
Which I first encountered when I was learning Clojure, and the scheme implementation of which was lifted from this gist: https://gist.github.com/emanon-was/ed12f6023e2d6328334a