Nextcloud and Guix System Server — GNUcode.me

Nextcloud and Guix System Server

by Joshua Branson — February 22, 2023

So I have wanted to run nextcloud for a while now. In my humble opinion, guix system makes maintaining websites super easy, so I would prefer to run nextcloud on guix system. Unfortunately, nextcloud will NOT be packaged in guix anytime soon for two reasons:

  1. Guix does not currently have a php build system or any php packages, though there is a 80% completed work-in-progress issue. So the php bits of nextcloud cannot be packaged properly.
  2. Nextcloud has a lot of javascript dependencies, and javascript is notoriously hard to package for guix.

It seems like the easiest way to currently run nextcloud on guix system is by using the all in one docker image. Please consider this a guide to set up running nextcloud on guix system via a linode, which currently costs me about $5 per month.

Note, that while this is the easiest method to run nextcloud, apparently this all in one docker image has some security issues:

The AIO image mounts the Docker socket, which is a security risk since it allows full access to other container as well as running any new container. It’s a bad idea and should be avoided.

tl;dr Here are the 6 simple steps that you need to do:

  1. Set up a linode guix system server. info "Guix Cookbook" RET i linode RET.

  2. Buy a domain name. I use hover.com.

  3. Point your domain name at your linode IP address.

  4. Set up a basic nginx static website without encryption. This means that you don’t want to define (service certbot-service-type).

    sudo mkdir -p /srv/www/html/yourdomainname.com
        
    # the command I did was this:
    sudo mkdir -p /srv/www/html/the-nx.com
        
    sudo chgrp -R users /srv
    sudo chmod -R g+rwx /srv

    Inside your newly created directory (srv/www/html/yourdomainname.com), put a simple HTML file and call it “index.html”. You could use this:

    <!doctype html>
    <html class="no-js" lang="">
        <head>
            <meta charset="utf-8">
            <meta http-equiv="x-ua-compatible" content="ie=edge">
            <title>the nx</title>
            <meta name="description" content="">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <link rel="apple-touch-icon" href="/apple-touch-icon.png">
        
        </head>
        <body>
            <!--[if lt IE 8]>
                <p class="browserupgrade">
                You are using an <strong>outdated</strong> browser. Please
                <a href="http://browsehappy.com/">upgrade your browser</a> to improve
                your experience.
                </p>
            <![endif]-->
        
            <p>Hello!</p>
        </body>
    </html>

    Now set up a basic nginx configuration for a static website without encryption. It will end up looking something like:

    (service nginx-service-type
             (nginx-configuration
              (server-blocks
               (list
                (nginx-server-configuration
                 (server-name '("the-nx.com"))
                 (listen (list "80" "[::]:80"))
                 (root "/srv/www/html/the-nx.com"))))))

    Now you need to reconfigure so that the nginx user is created:

    sudo guix system reconfigure config.scm

    Now, nginx is running, but you will probably need to give nginx access to read the files in your /srv directory.

    sudo chown -R nginx /srv
    sudo chmod -R u-rwx /srv

    Open up a web browser and go to http://yourdomainname.com and check to see that you see a basic website.

  5. Now you need to turn your basic static website, into a site that has https support. Now you need to edit your nginx config and add in a certbot config:

    Before your (operating-system ...) declartion, define this bit of code:

    (define %nginx-deploy-hook
      (program-file
       "nginx-deploy-hook"
       #~(let ((pid (call-with-input-file "/var/run/nginx/pid" read)))
           (kill pid SIGHUP))))

    Also make sure that you add in a certbot service and a modified nginx service that look like this:

    (service certbot-service-type
             (certbot-configuration
              (email "mysubscriptions@member.fsf.org")
              (webroot "/srv/www/")
              (certificates
               (list
                (certificate-configuration
                 (name "the-nx.com")
                 (domains '("the-nx.com" "www.the-nx.com"))
                 (deploy-hook %nginx-deploy-hook))))))
        
    (service nginx-service-type
             (nginx-configuration
              (server-blocks
               (list
                (nginx-server-configuration
                 (server-name '("the-nx.com"))
                 (listen (list "80"
                               "443 ssl http2"
                               "[::]:80"
                               "[::80]:443 ssl http2"))
                 (root "/srv/www/html/the-nx.com")
                 (ssl-certificate "/etc/letsencrypt/live/the-nx.com/fullchain.pem")
                 (ssl-certificate-key "/etc/letsencrypt/live/the-nx.com/privkey.pem")
                 (locations
                  (list
                   (nginx-location-configuration ;; for certbot
                    (uri "/.well-known")
                    (body (list "root /srv/www;"))))))))))

    Now we will have to reconfigure again to set up certbot:

    sudo guix system reconfigure config.scm
        
    # tell certbot to set up our certificates
    sudo /var/lib/certbot/renew-certificates

    Now you should be able to go to https://yourdomainname.com and see your site in glorious encrypted mode!

  6. Modify your guix config based on my the-nx.com-current-config.scm. You will need to enable these services (dbus-service), (service docker-service-type), (elogind service), (service certbot-service-type), and (service nginx-service-type).

I just ran this command, and my local nextcloud just started working.

sudo docker run \
--sig-proxy=false \
--name nextcloud-aio-mastercontainer \
--restart always \
--publish 80:80 \
--publish 8080:8080 \
--publish 8443:8443 \
--volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \
--volume /var/run/docker.sock:/var/run/docker.sock:ro \
nextcloud/all-in-one:latest

The following is the same quick guide as above, but has more details:

I decided to create a new linode image following the linode cookbook guide, and I noticed a tiny error in the guide:

sudo apt-get install gpg failed. It worked after I ran sudo apt-get update.

Also the basic config example needs to migrate to the new <swap-space> record. It gave me this warning message:

/root/config.scm:11:0: warning: List elements of the field ’swap-devices’ should now use the <swap-space> record, as the old method is deprecated. See “(guix) operating-system Reference” for more details.

The cookbook guide also should probably mention that you may need to login to the server for the first time using linode’s weblish, and set up the root passwd with passwd. Then set up your user password with passwd <username>.

Now that we have a basic site set up, let’s set up certbot and the nginx services:

(service certbot-service-type
         (certbot-configuration
          (email "mysubscriptions@member.fsf.org")
          (webroot "/srv/www/")
          (certificates
           (list
            (certificate-configuration
             (name "the-nx.com")
             (domains '("the-nx.com" "www.the-nx.com"))
             (deploy-hook %nginx-deploy-hook))))))

(nginx-configuration
 (server-blocks
  (list
   (nginx-server-configuration
    (server-name '("the-nx.com"))
    (listen (list "80"
                  "443 ssl http2"
                  ;;"[::]:80"
                  ;;"[::80]:443 ssl http2"
                  ))
    (root "/srv/www/html/the-nx.com")
    (ssl-certificate "/etc/letsencrypt/live/the-nx.com/fullchain.pem")
    (ssl-certificate-key "/etc/letsencrypt/live/the-nx.com/privkey.pem")
    (locations
     (list
      (nginx-location-configuration ;; for certbot
       (uri "/.well-known")
       (body (list "root /srv/www;")))))))))

Now let’s reconfigure and get a certbot certificate. ssh into the-nx.com and run these commands:

sudo guix system reconfigure the-nx.com-current-config.scm

# tell certbot to set up our certificates
sudo /var/lib/certbot/renew-certificates

So now my server has a valid certificate. It is time change the nginx configuration to proxy incoming requests to the docker all in one image.

Ok, maybe I can use sexpressions to tell nginx to redirect all incoming traffic to the-nx.com to the docker nextcloud image:

(nginx-location-configuration
 (uri "/")
 (body
  (list
   "proxy_pass http://127.0.0.1:9000;\n"
   "proxy_set_header X-Real-IP $remote_addr;\n"
   "proxy_set_header Host $host;\n"
   "proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n"
   "client_max_body_size 0;\n"
   "# Websocket\n"
   "proxy_http_version 1.1;\n"
   "proxy_set_header Upgrade $http_upgrade;\n")))

I am going to deploy this image, and take a look at the generated nginx configuration file. I ran this command on my T400 laptop:

guix deploy the-nx.com-current-config.scm

Well, that’s super annoying. I do not know which nginx.conf file is the right one:

find /gnu/store -name '*nginx.conf'

/gnu/store/7m1ygzqk6njn5mywqmhwbydbb2z4b9li-nginx.conf
/gnu/store/0gcfj61q4943h94jdqq7i9y0a0v9jr9q-nginx.conf
/gnu/store/4mzrp39w5i4v94kxf98gxc13ws79l88n-nginx.conf
/gnu/store/0nia2iqfw63ziasibbgq321wr9b3152n-nginx.conf
/gnu/store/pf8d0sj1yf9b2ndsbc61yj3h6rp4pck2-nginx.conf
/gnu/store/9nra62v41wsk08xf3msw5a1z35gji2gx-nginx-1.23.2/share/nginx/conf/nginx.conf
/gnu/store/4b1szfyn0snwzf3lm1snvaapk6diz3yq-nginx.conf
/gnu/store/fv5rg3nf5999vyg6qvp4sbgjysnkn1fc-nginx.conf
/gnu/store/vmjwj2zwblcz4wx2whsmxdfc7zxcgjh5-nginx.conf
/gnu/store/n3m2lihq9cjm6mxdln57q5nrbjgz53s6-nginx.conf
/gnu/store/jnl72hx0papzb42kbd1f19qx35w76lmg-nginx-1.23.2/share/nginx/conf/nginx.conf

I guess I will reboot, run guix system delete-generations and guix gc, and run the above command again:

find /gnu/store -name '*nginx.conf'

/gnu/store/7m1ygzqk6njn5mywqmhwbydbb2z4b9li-nginx.conf
/gnu/store/jnl72hx0papzb42kbd1f19qx35w76lmg-nginx-1.23.2/share/nginx/conf/nginx.conf

Well that looks promising. Let's check out my nginx.conf file.

cat /gnu/store/i2mzdhg8wlbxv7iza8y4qk5v0vmvp27q-nginx.conf

user nginx nginx;
pid /var/run/nginx/pid;
error_log /var/log/nginx/error.log info;
events { }
http {
    client_body_temp_path /var/run/nginx/client_body_temp;
    proxy_temp_path /var/run/nginx/proxy_temp;
    fastcgi_temp_path /var/run/nginx/fastcgi_temp;
    uwsgi_temp_path /var/run/nginx/uwsgi_temp;
    scgi_temp_path /var/run/nginx/scgi_temp;
    access_log /var/log/nginx/access.log;
    include /gnu/store/jnl72hx0papzb42kbd1f19qx35w76lmg-nginx-1.23.2/share/nginx/conf/mime.types;

    server {
      listen 443 ssl http2;
      server_name the-nx.com ;
      ssl_certificate /etc/letsencrypt/live/the-nx.com/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/the-nx.com/privkey.pem;
      root /srv/www/html/the-nx.com;
      index index.html ;
      server_tokens off;

      location /.well-known {
        root /srv/www;
      }
      location / {
        proxy_pass http://127.0.0.1:9000;

        proxy_set_header X-Real-IP $remote_addr;

        proxy_set_header Host $host;

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        client_max_body_size 0;

        # Websocket

        proxy_http_version 1.1;

        proxy_set_header Upgrade $http_upgrade;

      }

    }
    server {
      listen 80;
      listen [::]:80;
      server_name the-nx.com www.the-nx.com ;
      root /srv/http;
      index index.html ;
      server_tokens off;

      location /.well-known {
        root /srv/www/;
      }
      location / {
        return 301 https://$host$request_uri;
      }

    }

}

The generated configuration seems pretty wonky, and I am suprised that nginx is still running, but it is still running. And I suppose that it should work.

I was able to get nextcloud to start with this command:

sudo docker run --sig-proxy=false --name nextcloud-aio-mastercontainer \
 --restart always \
 --publish 8080:8080 \
 -e APACHE_PORT=9000 \
 --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \
 --volume /var/run/docker.sock:/var/run/docker.sock:ro  \
 nextcloud/all-in-one:latest

So now I can login at the-nx.com:8080 and configure various stuff. Also I really need to set up a firewall. That’s probably a really good idea. Also what’s nice about this docker image is that it will start itself if you update the guix system server and reboot.

MORE BONUS CONTENT:

If you see this blog post, and you decide to set up your nextcloud on a guix system server, and if your nginx config doesn’t seem to be proxying requests to your docker container, then you may follow these steps to delete the docker image and start over:

This page has some good commands for deleting the docker image and starting over:

sudo docker stop nextcloud-aio-mastercontainer && \\
sudo docker rm nextcloud-aio-mastercontainer && \\
sudo docker container prune -f && \\
sudo docker volume prune -f && \\
sudo docker pull nextcloud/all-in-one:latest

Ok, so it looks like the nextcloud all in one documentation has a page for understanding the reverse proxy.

It would also be nice to get my nextcloud image to sync my contacts. I probably just need to add in another nginx location line for that. That will be a project for another day.