summary refs log tree commit diff
path: root/topics/deploy
diff options
context:
space:
mode:
Diffstat (limited to 'topics/deploy')
-rw-r--r--topics/deploy/configuring-nginx-on-host.gmi220
-rw-r--r--topics/deploy/deployment.gmi9
-rw-r--r--topics/deploy/genecup.gmi69
-rw-r--r--topics/deploy/installation.gmi2
-rw-r--r--topics/deploy/machines.gmi10
-rw-r--r--topics/deploy/our-virtuoso-instances.gmi2
-rw-r--r--topics/deploy/paths-in-flask-applications.gmi22
-rw-r--r--topics/deploy/setting-up-or-migrating-production-across-machines.gmi202
-rw-r--r--topics/deploy/uthsc-email.gmi64
-rw-r--r--topics/deploy/uthsc-vpn-with-free-software.gmi68
-rw-r--r--topics/deploy/uthsc-vpn.scm154
11 files changed, 784 insertions, 38 deletions
diff --git a/topics/deploy/configuring-nginx-on-host.gmi b/topics/deploy/configuring-nginx-on-host.gmi
new file mode 100644
index 0000000..cb1c497
--- /dev/null
+++ b/topics/deploy/configuring-nginx-on-host.gmi
@@ -0,0 +1,220 @@
+# Configuring Nginx on the Host System
+
+## Tags
+
+* type: doc, docs, documentation
+* keywords: deploy, deployment, deploying, nginx, guix, guix container, guix system container
+* status: in progress
+
+## Introduction
+
+We deploy the GeneNetwork system within GNU Guix system containers. All the configurations and HTTPS certificates are handled from within the container, thus all the host has to do is to pass the traffic on to the system container.
+
+This document shows you how to set up the host container to forward all the necessary traffic so that you do not run into all the problems that we did when figuring this stuff out :-).
+
+## Ports and Domains
+
+In your system container, there are certain ports that are defined for various traffic. The most important ones, and the ones we will deal with, are for HTTP and HTTPS. The ideas should translate for most other ports.
+
+For the examples is this document, we will assume the following ports are defined in the Guix system container:
+* HTTP on port 9080
+* HTTPS on port 9081
+
+## HTTPS Traffic
+
+### Nginx --with-stream_ssl_preread_module
+
+We handle all the necessary traffic details (e.g. SSL/TLS termination, etc.) within the container, and only need the host to forward the traffic.
+
+In order to achieve this, your Nginx will need to be compiled with the
+=> https://nginx.org/en/docs/stream/ngx_stream_ssl_preread_module.html Nginx Stream SSL Preread Module.
+
+Now, because we are awesome, we include
+=> https://git.genenetwork.org/gn-machines/tree/nginx-preread.scm a definition for nginx compiled with the module.
+Simply install it on your host by doing something like:
+
+```
+$ git clone https://git.genenetwork.org/gn-machines
+$ cd gn-machines
+$ ./nginx-preread-deploy.sh
+```
+
+That will install the nginx under "/usr/local/sbin/nginx".
+
+Now, we comment out, or delete any/all lines loading any nginx modules for any previously existing nginx. Comment out/delete the following line in your "/etc/nginx/nginx.conf" file if it exists:
+
+```
+include /etc/nginx/modules-enabled/*.conf;
+```
+
+This is necessary since the nginx we installed from guix comes with all the modules we need, and even if not, it would not successfully use the hosts modules anyhow. You'd need to modify the nginx config for yourself to add any missing modules for the nginx from guix — how to do that is outside the scope of this document, but should not be particularly difficult.
+
+Set up your init system to use the nginx from guix. Assuming systemd, you need to have something like the following in your "/etc/systemd/system/nginx.service" unit file:
+
+```
+[Unit]
+Description=nginx web server (from Guix, not the host)
+After=network.target
+
+[Service]
+Type=forking
+PIDFile=/run/nginx.pid
+ExecStartPre=/usr/local/sbin/nginx -q -t -c /etc/nginx/nginx.conf -e /var/log/nginx/error.log
+ExecStart=/usr/local/sbin/nginx -c /etc/nginx/nginx.conf -p /var/run/nginx -e /var/log/nginx/error.log
+ExecReload=/usr/local/sbin/nginx -c /etc/nginx/nginx.conf -s reload -e /var/log/nginx/error.log
+ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid
+TimeoutStopSec=5
+KillMode=mixed
+
+[Install]
+WantedBy=multi-user.target
+```
+
+Awesome. Now enable the unit file:
+
+```
+$ sudo systemctl enable nginx.service
+```
+
+### Forwarding the HTTPS Traffic
+
+Now that we have nginx in place, we can forward HTTPS traffic for all the domains we want. In "/etc/nginx/nginx.conf" we add:
+
+```
+# Forward some HTTPS connections into existing guix containers
+stream {
+    upstream my-container {
+        # This is our Guix system container
+        server 127.0.0.1:9081;
+    }
+
+    upstream host-https {
+        # Forward any https traffic for any previously existing domains on the
+        # host itself.
+        server 127.0.0.1:6443;
+    }
+
+    map $ssl_preread_server_name $upstream {
+        yourdomain1.genenetwork.org my-container;
+        yourdomain2.genenetwork.org my-container;
+        default host-https;
+    }
+
+    server {
+        listen 443;
+        proxy_pass $upstream;
+        ssl_preread on;
+    }
+}
+```
+
+## HTTP Traffic
+
+You will need to pass the HTTP traffic on to the container in order to enable HTTP-dependent traffic (e.g. setting up the SSL certificates using the ACME protocol) is successfully handled.
+
+You have 2 options to do this:
+* Add a separate server block in `/etc/nginx/site-available/` (or other configured directory)
+* Add the server block directly in `/etc/nginx/nginx.conf` (or your main nginx config file, if it's not the standard one mentioned here).
+
+The configuration to add is as follows:
+
+```
+server {
+    ## Forward HTTP traffic to container
+    ## Without this, the HTTP calls will fall through to the defaults in
+    ## /etc/nginx/sites-enabled/ leading to http-dependent traffic, like
+    ## that of the ACME client, failing.
+    server_name yourdomain1.genenetwork.org yourdomain2.genenetwork.org …;
+    listen 80;
+    location / {
+        proxy_pass http://127.0.0.1:9080;
+        proxy_set_header Host $host;
+    }
+}
+```
+
+** Do please replace the "yourdomain*" parts in the example above as appropriate for your scenario. The ellipsis (…) indicate optional extra domains you might need to configure.
+
+Without this, the `Run ACME Client` below will fail
+
+## Run ACME Client
+
+Now that all traffic is set up, and you can reach your sites using both HTTP and HTTPS (you have tested your sites, right? right?) we can now request the SSL certificates from Let's Encrypt so that we no longer see the "Self-signed Certificate" warning.
+
+You need to get into your system container to do this. The steps are a follows:
+
+=> https://git.genenetwork.org/gn-machines/tree/README.org#n61 Figure out which process is your container
+=> https://git.genenetwork.org/gn-machines/tree/README.org#n55 Get a shell into the container
+=> https://guix-forge.systemreboot.net/manual/dev/en/#section-acme-service Run "/usr/bin/acme renew" to get your initial SSL certificates from Let's Encrypt.
+
+At this point, the traffic portion of the configuration is done.
+
+## Sample "/etc/nginx/nginx.conf"
+
+```
+user www-data;
+worker_processes auto;
+pid /run/nginx.pid;
+# include /etc/nginx/modules-enabled/*.conf;
+
+access_log /var/log/nginx/access.log;
+error_log /var/log/nginx/error.log error;
+
+events {
+    worker_connections 768;
+    # multi_accept on;
+}
+
+stream {
+    upstream my-container {
+        # This is our Guix system container
+        server 127.0.0.1:9081;
+    }
+
+    upstream host-https {
+        # Forward any https traffic for any previously existing domains on the
+        # host itself.
+        server 127.0.0.1:6443;
+    }
+
+    map $ssl_preread_server_name $upstream {
+        yourdomain1.genenetwork.org my-container;
+        yourdomain2.genenetwork.org my-container;
+        default host-https;
+    }
+
+    server {
+        listen 443;
+        proxy_pass $upstream;
+        ssl_preread on;
+    }
+}
+
+http {
+    ##
+    # Basic Settings
+    ##
+    
+    ⋮
+    
+    include /etc/nginx/conf.d/*.conf;
+    server {
+        ## Forward HTTP traffic to container
+        ## Without this, the HTTP calls will fall through to the defaults in
+        ## /etc/nginx/sites-enabled/ leading to http-dependent traffic, like
+        ## that of the ACME client, failing.
+        server_name yourdomain1.genenetwork.org yourdomain2.genenetwork.org …;
+        listen 80;
+        location / {
+            proxy_pass http://127.0.0.1:9080;
+            proxy_set_header Host $host;
+        }
+    }
+    include /etc/nginx/sites-enabled/*;
+    
+    ⋮
+}
+
+⋮
+
+```
diff --git a/topics/deploy/deployment.gmi b/topics/deploy/deployment.gmi
index b844821..74fd6f0 100644
--- a/topics/deploy/deployment.gmi
+++ b/topics/deploy/deployment.gmi
@@ -1,14 +1,21 @@
 # Deploy GeneNetwork
 
+## Tags
+
+* type: doc, docs, documentation
+* keywords: deploy, deployment, deploying, guix, guix container, guix system container
+* status: in progress
+
 # Description
 
 This page attempts to document the deployment process we have for GeneNetwork. We use Guix system containers for deployment of CI/CD and the Guix configuration for the CI/CD container should be considered the authoritative reference.
 
-=> https://github.com/genenetwork/genenetwork-machines/blob/main/genenetwork-development.scm
+=> https://git.genenetwork.org/gn-machines/tree/genenetwork-development.scm
 
 See also
 
 => ./guix-system-containers-and-how-we-use-them
+=> ./configuring-nginx-on-host
 
 ## genenetwork2
 
diff --git a/topics/deploy/genecup.gmi b/topics/deploy/genecup.gmi
index c5aec17..fc93d07 100644
--- a/topics/deploy/genecup.gmi
+++ b/topics/deploy/genecup.gmi
@@ -53,3 +53,72 @@ and port forward:
 ssh -L 4200:127.0.0.1:4200 -f -N server
 curl localhost:4200
 ```
+
+# Troubleshooting
+
+## Moving the PubMed dir
+
+After moving the PubMed dir GeneCup stopped displaying part of the connections. This can be reproduced by running the standard example on the home page - the result should look like the image on the right of the home page.
+
+After fixing the paths and restarting the service there still was no result.
+
+Genecup is currently managed by the shepherd as user shepherd. Stop the service as that user:
+
+```
+shepherd@tux02:~$ herd stop genecup
+guile: warning: failed to install locale
+Service genecup has been stopped.
+```
+
+Now the servic looks stopped, but it is still running and you need to kill by hand:
+
+```
+shepherd@tux02:~$ ps xau|grep genecup
+shepherd  89524  0.0  0.0  12780   944 pts/42   S+   00:32   0:00 grep genecup
+shepherd 129334  0.0  0.7 42620944 2089640 ?    Sl   Mar05  66:30 /gnu/store/1w5v338qk5m8khcazwclprs3znqp6f7f-python-3.10.7/bin/python3 /gnu/store/a6z0mmj6iq6grwynfvkzd0xbbr4zdm0l-genecup-latest-with-tensorflow-native-HEAD-of-master-branch/.server.py-real
+shepherd@tux02:~$ kill -9 129334
+shepherd@tux02:~$ ps xau|grep genecup
+shepherd  89747  0.0  0.0  12780   944 pts/42   S+   00:32   0:00 grep genecup
+shepherd@tux02:~$
+```
+
+The log file lives in
+
+```
+shepherd@tux02:~/logs$ tail -f genecup.log
+```
+
+and we were getting errors on a reload and I had to fix
+
+```
+shepherd@tux02:~/shepherd-services$ grep export run_genecup.sh
+export EDIRECT_PUBMED_MASTER=/export3/PubMed
+export TMPDIR=/export/ratspub/tmp
+export NLTK_DATA=/export3/PubMed/nltk_data
+```
+
+See
+
+=> https://git.genenetwork.org/gn-shepherd-services/commit/?id=cd4512634ce1407b14b0842b0ef6a9cd35e6d46c
+
+The symlink from /export2 is not honoured by the guix container. Now the service works.
+
+Note we have deprecation warnings that need to be addressed in the future:
+
+```
+2025-04-22 00:40:07 /home/shepherd/services/genecup/guix-past/modules/past/packages/python.scm:740:19: warning: 'texlive-union' is deprecated,
+ use 'texlive-updmap.cfg' instead
+2025-04-22 00:40:07 guix build: warning: 'texlive-latex-base' is deprecated, use 'texlive-latex-bin' instead
+2025-04-22 00:40:15 updating checkout of 'https://git.genenetwork.org/genecup'...
+/gnu/store/9lbn1l04y0xciasv6zzigqrrk1bzz543-tensorflow-native-1.9.0/lib/python3.10/site-packages/tensorflow/python/framewo
+rk/dtypes.py:526: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.
+2025-04-22 00:40:38   _np_quint16 = np.dtype([("quint16", np.uint16, 1)])
+2025-04-22 00:40:38 /gnu/store/9lbn1l04y0xciasv6zzigqrrk1bzz543-tensorflow-native-1.9.0/lib/python3.10/site-packages/tensorflow/python/framewo
+rk/dtypes.py:527: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.
+2025-04-22 00:40:38   _np_qint32 = np.dtype([("qint32", np.int32, 1)])
+2025-04-22 00:40:38 /gnu/store/9lbn1l04y0xciasv6zzigqrrk1bzz543-tensorflow-native-1.9.0/lib/python3.10/site-packages/tensorflow/python/framewo
+rk/dtypes.py:532: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.
+2025-04-22 00:40:38   np_resource = np.dtype([("resource", np.ubyte, 1)])
+2025-04-22 00:40:39 /gnu/store/7sam0mr9kxrd4p7g1hlz9wrwag67a6x6-python-flask-sqlalchemy-2.5.1/lib/python3.10/site-packages/flask_sqlalchemy/__
+init__.py:872: FSADeprecationWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future. Set it to True or False to suppress this warning.
+```
diff --git a/topics/deploy/installation.gmi b/topics/deploy/installation.gmi
index 757d848..d6baa79 100644
--- a/topics/deploy/installation.gmi
+++ b/topics/deploy/installation.gmi
@@ -319,7 +319,7 @@ Currently we have two databases for deployment,
 from BXD mice and 'db_webqtl_plant' which contains all plant related
 material.
 
-Download one database from
+Download a recent database from
 
 => https://files.genenetwork.org/database/
 
diff --git a/topics/deploy/machines.gmi b/topics/deploy/machines.gmi
index d610c9f..a7c197c 100644
--- a/topics/deploy/machines.gmi
+++ b/topics/deploy/machines.gmi
@@ -2,17 +2,19 @@
 
 ```
 - [ ] bacchus             172.23.17.156   (00:11:32:ba:7f:17) -  1 Gbs
-- [X] lambda01            172.23.18.212   (7c:c2:55:11:9c:ac)
+- [ ] penguin2
+- [X] lambda01            172.23.18.212   (7c:c2:55:11:9c:ac) - currently 172.23.17.41
 - [X] tux03i              172.23.17.181   (00:0a:f7:c1:00:8d) - 10 Gbs
   [X] tux03               128.169.5.101   (00:0a:f7:c1:00:8b) -  1 Gbs
-- [ ] tux04i              172.23.17.170   (14:23:f2:4f:e6:10)
-- [ ] tux04               128.169.5.119   (14:23:f2:4f:e6:11)
+- [X] tux04i              172.23.17.170   (14:23:f2:4f:e6:10)
+- [X] tux04               128.169.5.119   (14:23:f2:4f:e6:11)
 - [X] tux05               172.23.18.129   (14:23:f2:4f:35:00)
 - [X] tux06               172.23.17.188   (14:23:f2:4e:29:10)
 - [X] tux07               172.23.17.191   (14:23:f2:4e:7d:60)
 - [X] tux08               172.23.17.186   (14:23:f2:4f:4e:b0)
 - [X] tux09               172.23.17.182   (14:23:f2:4e:49:10)
 - [X] space               128.169.5.175   (e4:3d:1a:80:6c:40)
+- [ ] space-i             172.23.18.153   (cc:48:3a:13:db:4c)
 - [ ] octopus01f          172.23.18.221   (2c:ea:7f:60:bf:61)
 - [ ] octopus02f          172.23.22.159   (2c:ea:7f:60:bd:61)
 - [ ] octopus03f          172.23.19.187   (2c:ea:7f:60:ac:2b)
@@ -25,6 +27,8 @@ c for console or control
 
 ```
 - [ ] DNS entries no longer visible
+- [X] penguin2-c        172.23.31.83
+- [ ] octolair01        172.23.16.228
 - [X] lambda01-c        172.23.17.173   (3c:ec:ef:aa:e5:50)
 - [X] tux01-c           172.23.31.85    (58:8A:5A:F9:3A:22)
 - [X] tux02-c           172.23.30.40    (58:8A:5A:F0:E6:E4)
diff --git a/topics/deploy/our-virtuoso-instances.gmi b/topics/deploy/our-virtuoso-instances.gmi
index 0336018..3ac56ae 100644
--- a/topics/deploy/our-virtuoso-instances.gmi
+++ b/topics/deploy/our-virtuoso-instances.gmi
@@ -9,6 +9,8 @@ We run three instances of virtuoso.
 The public SPARQL endpoint is accessible at
 => https://sparql.genenetwork.org/sparql
 
+These are now generally run as part of genenetwork2 containers(!)
+
 ## Configuration
 
 All our virtuoso instances are deployed in Guix system containers. The configuration for these containers is at
diff --git a/topics/deploy/paths-in-flask-applications.gmi b/topics/deploy/paths-in-flask-applications.gmi
new file mode 100644
index 0000000..77bc201
--- /dev/null
+++ b/topics/deploy/paths-in-flask-applications.gmi
@@ -0,0 +1,22 @@
+# Paths in Flask Application
+
+## Tags
+
+* type: doc, docs, documentation
+* assigned: fredm
+* keywords: application paths, flask, absolute path, relative path
+
+## Content
+
+Always build and use absolute paths for the resources you use in your application. Assuming that the application will always be run with the root of the application's repository/package as the working directory is a recipe for failure.
+
+To demonstrate, see the following issue:
+=> /issues/genenetwork2/haley-knott-regression-mapping-error
+
+In this case, the path issue was not caught in the CI/CD environment since it runs the application with the repository root as its working directory. This issue will also not show up in most development environments since it is easier to run the application from the root of the repository than have to set up the PYTHONPATH variables.
+
+In the new containers making use of the "(genenetwork services genenetwork)" module in gn-machines[fn:1], the working directory where the application is invoked has no relation with the application's package — in fact, the working directory is actually the root of the containers file system ("/").
+
+# Footnotes
+
+[fn:1] https://git.genenetwork.org/gn-machines/
diff --git a/topics/deploy/setting-up-or-migrating-production-across-machines.gmi b/topics/deploy/setting-up-or-migrating-production-across-machines.gmi
new file mode 100644
index 0000000..631a000
--- /dev/null
+++ b/topics/deploy/setting-up-or-migrating-production-across-machines.gmi
@@ -0,0 +1,202 @@
+# Setting Up or Migrating Production Across Machines
+
+## Tags
+
+* type: documentation, docs, doc
+* status: in-progress
+* assigned: fredm
+* priority: undefined
+* keywords: migration, production, genenetwork
+* interested-parties: pjotrp, zachs
+
+## Introduction
+
+Recent events (Late 2024 and early 2025) have led to us needing to move the production system from one machine to the other several time, due to machine failures, disk space, security concerns, and the like.
+
+In this respect, a number of tasks rise to the front as necessary to accomplish for a successful migration. Each of the following sections will detail a task that's necessary for a successful migration.
+
+## Copy Over Auth Database
+
+We need to synchronise the authorisation database. We can copy this over from the production system, or the backups
+
+* TODO: Indicate where the backups for the auth database are here!
+
+Steps (flesh out better):
+
+* Extract backup (or copy from existing production system)
+* Stop the (new) container (if it's running)
+* Backup the (new) container's auth-db file (
+* Place the auth db file in the correct place in the container's filesystem:
+* Backup existing secrets
+* Login to the `/auth/admin/dashboard` of the auth server (e.g. https://cd.genenetwork.org/auth/admin/dashboard)
+* If client with the CLIENT_ID in the secrets exists
+* 1. update the uris for that client, if it doesn't exist, create an entirely new client and replace both the CLIENT_ID and CLIENT_SECRET in the secrets file.
+* 2. Click on the "Change Secret" button and generate a new secret. Replace the secret in the secrets file with the newly generated secret
+* If client with the CLIENT_ID in the secrets DOES NOT exist, register a new client, setting up the appropriate URIs and endpoints, and then add/replace both the CLIENT_ID and CLIENT_SECRET in the secrets file.
+* Restart (new) container
+
+## Set Up the Database
+
+=> /topics/systems/restore-backups Extract the latest database from the backups.
+=> /topics/deploy/installation Configure MariaDB according to this document.
+
+## Set Up the File System
+
+* TODO: List the necessary directories and describe what purpose each serves. This will be from the perspective of the container — actual paths on the host system are left to the builders choice, and can vary wildly.
+* TODO: Prefer explicit binding rather than implicit — makes the shell scripts longer, but no assumptions have to be made, everything is explicitly spelled out.
+
+The container(s) need access to various files and directories from the host system in order to work correctly.
+
+Filesystem bindings could be linked to wildly different paths on different physical host machines, therefore, we shall examine the bindings from the point of view of the paths within the container, rather than forcing a particular file system layout on the host systems themselves.
+
+Each of the sections below details a specific binding:
+
+### /var/genenetwork
+
+This binding must be READWRITE within the container.
+
+The purpose is to hold varying files that are specific to the genenetwork system(s). Examples of the files are:
+
+* "gn-meta" and "synteny" files for GN3
+* genotype files
+* session files for various systems (GN2, gn-uploader, etc.)
+
+### /var/lib/acme
+
+This binding must be READWRITE within the container.
+
+This is used to store TLS certificates for the various services within the container by the ACME (Automatic Certificate Management Environment) script.
+
+### /var/lib/redis
+
+This binding must be READWRITE within the container.
+
+This is used by the redis daemon to persist its state(s).
+
+### /var/lib/virtuoso
+
+This binding must be READWRITE within the container.
+
+Used by the virtuoso daemon to save its state, and maybe some log files.
+
+### /export/data/virtuoso/
+
+This binding must be READONLY within the container. (Really?)
+
+This is used for importing data into virtuoso, say by sharing Turtle (TTL) files within the binding.
+
+---
+At this point the binding is READONLY because any TTL files to load are imported from outside the container. If the transformation of data from MariaDB to TTL form is built into the production containers down the line, then this might change to READWRITE to allow the tranformation tool to write to it.
+
+### /var/log
+
+This binding must be READWRITE within the container.
+
+Allows logs from various services running in the container be accessible in the host system. This is useful for debugging issues with the running systems.
+
+### /etc/genenetwork
+
+This binding must be READWRITE within the container.
+
+Useful for storing various configuration files/data for the service(s) running inside the running container.
+
+### /var/lib/xapian
+
+This binding must be READWRITE within the container.
+
+Stores the processed search indexes for the xapian search system.
+
+### /var/lib/genenetwork/sqlite/gn-auth
+
+This binding must be READWRITE within the container.
+
+The authorisation database is stored here. The directory needs to be writable to avoid permissions issues within the container when attempting to write data into the database.
+
+### /var/lib/genenetwork/sqlite/genenetwork3
+
+This binding must be READWRITE within the container.
+
+This stores various SQLite databases in use with GN3. These are:
+
+* Database for the GNQA system
+* ...
+
+### /run/mysqld
+
+This binding must be READWRITE within the container.
+
+This binding is the link to the host directory that holds the socket file for the running MariaDB instance.
+
+### /opt/gn/tmp
+
+This binding must be READWRITE within the container.
+
+Holds temporary files for the various services that run within the container. Some of the generated files from various services are also stored here.
+
+**PROPOSAL**: Move all generated files here, or have a dedicated directory for holding generated files?
+
+
+### /var/genenetwork/sessions
+
+This binding must be READWRITE within the container.
+
+Holds session files for various services within the container. See also the /var/genenetwork binding.
+
+### /var/lib/genenetwork/uploader
+
+This binding must be READWRITE within the container.
+
+**gn-uploader** specific data files. Types of data files that could go here are:
+
+* File uploads
+* (Reusable) Cache files and generated files
+* ... others?
+
+### /var/lib/genenetwork/sqlite/gn-uploader
+
+This binding must be READWRITE within the container.
+
+Holds various SQLite databases used with the **gn-uploader** service, e.g.:
+
+* Background jobs database
+* ...
+
+### /var/lib/genenetwork/gn-guile
+
+This binding must be READWRITE within the container.
+
+Various data files for the **gn-guile** service, such as:
+
+* The bare **gn-docs** repository (Previously bound at `/export/data/gn-docs`: now deprecated).
+
+## Redis
+
+We currently (2025-06-11) use Redis for:
+
+* Tracking user collection (this will be moved to SQLite database)
+* Tracking background jobs (this is being moved out to SQLite databases)
+* Tracking running-time (not sure what this is about)
+* Others?
+
+We do need to copy over the redis save file whenever we do a migration, at least until the user collections and background jobs features have been moved completely out of Redis.
+
+## Container Configurations: Secrets
+
+* TODO: Detail how to extract/restore the existing secrets configurations in the new machine
+
+## Build Production Container
+
+* TODO: Add notes on building
+* TODO: Add notes on setting up systemd
+
+## NGINX
+
+* TODO: Add notes on streaming and configuration of it thereof
+
+## SSL Certificates
+
+* TODO: Add notes on acquisition and setup of SSL certificates
+
+## DNS
+
+* TODO: Migrate DNS settings
diff --git a/topics/deploy/uthsc-email.gmi b/topics/deploy/uthsc-email.gmi
new file mode 100644
index 0000000..05f2ba5
--- /dev/null
+++ b/topics/deploy/uthsc-email.gmi
@@ -0,0 +1,64 @@
+# UTHSC E-mail
+
+Similar to many organizations UT uses outlook and exchange for their E-mail. Thanks to mobile support it is possible to work with email using other tools outside the UT network.
+
+## Prospect E-mail client
+
+People have success using Prospect as an E-mail client. You can follow the instructions on the UT website that are similar to Android support.
+
+## Davmail IMAP bridge
+
+An interesting solution is to create an IMAP bridge. It is a little slower, but can be set up! That way you can use your favorite E-mail tool (and filters!).
+
+I have had success setting up davmail with the following settings and testing with thunderbird first:
+
+```
+apt install davmail openjfx thunderbird
+```
+
+Start davmail with
+
+```
+davmail -d
+```
+
+Stop davmail and edit the ~/.davmail.properties file with the following.
+
+```
+davmail.mode=O365Interactive
+davmail.url=https://outlook.office365.com/EWS/Exchange.asmx
+davmail.oauth.clientId=d3590ed6-52b3-4102-aeff-aad2292ab01c
+davmail.enableOauth2=true
+davmail.oauth.deviceCode=true
+davmail.oauth.enableOauth2=true
+davmail.oauth.redirectUri=urn:ietf:wg:oauth:2.0:oob
+davmail.oauth.tenantId=common
+davmail.imapPort=1143
+davmail.smtpPort=1025
+davmail.logFilePath=/home/yours/.davmail/davmail.log
+log4j.logger.httpclient.wire=DEBUG
+log4j.rootLogger=DEBUG
+log4j.logger.org.apache.http.wire=DEBUG
+```
+
+Restart davmail and point thunderbird to
+
+```
+IMAP Server: localhost:1143
+SMTP Server: localhost:1025
+Username: your-email@uthsc.edu
+```
+
+Note that you should set the UT password in the 2FA browser when it pops up. Do *not* set it in Thunderbird, also when it asks for it to send out SMTP.
+
+When something fails make sure to track the log in ~/.davmail/davmail.log
+
+# Using Mutt
+
+Some useful links:
+
+=> https://jonathanh.co.uk/blog/exchange-mutt/
+=> https://movementarian.org/blog/posts/mutt-and-office365/
+=> https://www.vanormondt.net/~peter/blog/2021-03-16-mutt-office365-mfa.html
+
+If someone can get the last one to work we won't even need davmail any more!
diff --git a/topics/deploy/uthsc-vpn-with-free-software.gmi b/topics/deploy/uthsc-vpn-with-free-software.gmi
index 344772c..aeba322 100644
--- a/topics/deploy/uthsc-vpn-with-free-software.gmi
+++ b/topics/deploy/uthsc-vpn-with-free-software.gmi
@@ -6,10 +6,24 @@ It is possible to connect to the UTHSC VPN using only free software. For this, y
 
 To connect, run openconnect-sso as follows. A browser window will pop up for you to complete the Duo authentication. Once done, you will be connected to the VPN.
 ```
-$ openconnect-sso --server uthscvpn1.uthsc.edu --authgroup UTHSC
+$ openconnect-sso --server vpn-server --authgroup UTHSC
 ```
 Note that openconnect-sso should be run as a regular user, not as root. After passing Duo authentication, openconnect-sso will try to gain root priviliges to set up the network routes. At that point, it will prompt you for your password using sudo.
 
+## Recommended way
+
+The recommended way is to use Arun's g-expression setup using guix. See below. It should just work, provided you have the chained certificate that you can get from the browser or one of us and point to the right server. Simply
+
+```
+$(guix build -f uthsc-vpn.scm)
+```
+
+See
+
+=> ./uthsc-vpn.scm
+
+Get the final details from us. UT does not like it when we put it online even though there is no real risk.
+
 ## Avoid tunneling all your network traffic through the VPN (aka Split Tunneling)
 
 openconnect, by default, tunnels all your traffic through the VPN. This is not good for your privacy. It is better to tunnel only the traffic destined to the specific hosts that you want to access. This can be done using the vpn-slice script.
@@ -17,7 +31,7 @@ openconnect, by default, tunnels all your traffic through the VPN. This is not g
 
 For example, to connect to the UTHSC VPN but only access the hosts tux01 and tux02e through the VPN, run the following command.
 ```
-$ openconnect-sso --server uthscvpn1.uthsc.edu --authgroup UTHSC -- --script 'vpn-slice tux01 tux02e'
+$ openconnect-sso --server vpn-server --authgroup UTHSC -- --script 'vpn-slice tux01 tux02e'
 ```
 The vpn-slice script looks up the hostnames tux01 and tux02e on the VPN DNS and adds /etc/hosts entries and routes to your system. vpn-slice can also set up more complicated routes. To learn more, read the vpn-slice documentation.
 
@@ -44,50 +58,50 @@ export OPENSSL_CONF=/tmp/openssl.cnf
 ```
 Then, run the openconnect-sso client as usual.
 
-## Putting it all together using Guix G-expressions
+## Misconfigured UTHSC TLS certificate
 
-Remembering to do all these steps is a hassle. Writing a shell script to automate this is a good idea, but why write shell scripts when we have G-expressions! Here's a G-expression script that I prepared earlier.
-=> uthsc-vpn.scm
-Download it, tweak the %hosts variable to specify the hosts you are interested in, and run it like so:
+The UTHSC TLS certificate does not validate on some systems. You can work around this by downloading the certificate chain and adding it to your system:
+* Navigate with browser to https://vpn-server/. Inspect the certificate in the browser (lock icon next to search bar) and export .pem file
+* Move it to /usr/local/share/ca-certificates (with .crt extension) or equivalent
+* On Debian/Ubuntu update the certificate store with update-ca-certificates
+You should see
 ```
-$(guix build -f uthsc-vpn.scm)
+Updating certificates in /etc/ssl/certs...
+1 added, 0 removed; done.
 ```
+Thanks Niklas. See also
+=> https://superuser.com/a/719047/914881
 
-# Troubleshooting
-
-Older versions would not show a proper dialog for sign-in. Try
-
+However, adding certificates to your system manually is not good security practice. It is better to limit the added certificate to the openconnect process. You can do this using the REQUESTS_CA_BUNDLE environment variable like so:
 ```
-export QTWEBENGINE_CHROMIUM_FLAGS=--disable-seccomp-filter-sandbox
+REQUESTS_CA_BUNDLE=/path/to/uthsc/certificate.pem openconnect-sso --server vpn-server --authgroup UTHSC
 ```
 
-## Update certificate
-
-When the certificate expires you can download the new one with:
+## Putting it all together using Guix G-expressions
 
-* Navigate with browser to https://uthscvpn1.uthsc.edu/. Inspect the certificate in the browser (lock icon next to search bar) and export .pem file
-* Move it to /usr/local/share/ca-certificates (with .crt extension) or equivalent
-* On Debian/Ubuntu update the certificate store with update-ca-certificates
+Remembering to do all these steps is a hassle. Writing a shell script to automate this is a good idea, but why write shell scripts when we have G-expressions! Here's a G-expression script that I prepared earlier.
+=> uthsc-vpn.scm
+Download it, download the UTHSC TLS certificate chain to uthsc-certificate.pem, tweak the %hosts variable to specify the hosts you are interested in, and run it like so:
+```
+$(guix build -f uthsc-vpn.scm)
+```
 
-You should see
+to add a route by hand after you can do
 
 ```
-Updating certificates in /etc/ssl/certs...
-1 added, 0 removed; done.
+ip route add 172.23.17.156 dev tun0
 ```
 
-Thanks Niklas. See also
-
-=> https://superuser.com/a/719047/914881
+# Troubleshooting
 
-On GUIX you may need to point to the updated certificates file with:
+Older versions would not show a proper dialog for sign-in. Try
 
 ```
-env REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt  openconnect-sso --server uthscvpn1.uthsc.edu --authgroup UTHSC
+export QTWEBENGINE_CHROMIUM_FLAGS=--disable-seccomp-filter-sandbox
 ```
 
 ## Acknowledgement
 
-Many thanks to Pjotr Prins and Erik Garrison without whose earlier work this guide would not be possible.
+Many thanks to Arun, Pjotr and Erik without whose earlier work this guide would not be possible.
 => https://github.com/pjotrp/linux-at-university-of-tennessee
 => https://github.com/ekg/openconnect-sso-docker
diff --git a/topics/deploy/uthsc-vpn.scm b/topics/deploy/uthsc-vpn.scm
index c714731..f204cdf 100644
--- a/topics/deploy/uthsc-vpn.scm
+++ b/topics/deploy/uthsc-vpn.scm
@@ -1,11 +1,30 @@
-(use-modules ((gnu packages guile-xyz) #:select (guile-ini guile-lib guile-smc))
-             ((gnu packages vpn) #:select (openconnect-sso vpn-slice))
-             (guix gexp))
+(use-modules ((gnu packages check)
+              #:select (python-pytest python-pytest-asyncio python-pytest-httpserver))
+             ((gnu packages freedesktop) #:select (python-pyxdg))
+             ((gnu packages python-build) #:select (python-poetry-core python-toml))
+             ((gnu packages python-crypto)
+              #:select (python-keyring python-pyotp))
+             ((gnu packages python-web) #:select (python-requests python-urllib3))
+             ((gnu packages python-xyz)
+              #:select (python-attrs python-charset-normalizer
+                                     python-colorama python-prompt-toolkit python-pysocks
+                                     python-structlog))
+             ((gnu packages guile-xyz) #:select (guile-ini guile-lib guile-smc))
+             ((gnu packages qt) #:select (python-pyqt-6 python-pyqtwebengine-6))
+             ((gnu packages vpn) #:select (openconnect vpn-slice))
+             ((gnu packages xml) #:select (python-lxml-4.9))
+             (guix build-system pyproject)
+             (guix build-system python)
+             (guix download)
+             (guix gexp)
+             (guix git-download)
+             ((guix licenses) #:prefix license:)
+             (guix packages))
 
 ;; Put in the hosts you are interested in here.
 (define %hosts
   (list "octopus01"
-        "tux01.genenetwork.org"))
+        "spacex"))
 
 (define (ini-file name scm)
   "Return a file-like object representing INI file with @var{name} and
@@ -19,6 +38,127 @@
                        (call-with-output-file #$output
                          (cut scm->ini #$scm #:port <>))))))
 
+(define python-urllib3-1.26
+  (package
+    (inherit python-urllib3)
+    (version "1.26.15")
+    (source
+     (origin
+       (method url-fetch)
+       (uri (pypi-uri "urllib3" version))
+       (sha256
+        (base32
+         "01dkqv0rsjqyw4wrp6yj8h3bcnl7c678qkj845596vs7p4bqff4a"))))
+    (build-system python-build-system)))
+
+(define python-charset-normalizer-2.10
+  (package
+    (inherit python-charset-normalizer)
+    (version "2.1.0")
+    (source
+     (origin
+       (method url-fetch)
+       (uri (pypi-uri "charset-normalizer" version))
+       (sha256
+        (base32 "04zlajr77f6c7ai59l46as1idi0jjgbvj72lh4v5wfpz2s070pjp"))))
+    (build-system python-build-system)
+    (arguments (list))
+    (native-inputs
+     (modify-inputs (package-native-inputs python-charset-normalizer)
+       (delete "python-setuptools")))))
+
+(define python-requests-2.28
+  (package
+    (inherit python-requests)
+    (name "python-requests")
+    (version "2.28.1")
+    (source (origin
+              (method url-fetch)
+              (uri (pypi-uri "requests" version))
+              (sha256
+               (base32
+                "10vrr7bijzrypvms3g2sgz8vya7f9ymmcv423ikampgy0aqrjmbw"))))
+    (build-system python-build-system)
+    (arguments (list #:tests? #f))
+    (native-inputs (list))
+    (propagated-inputs
+     (modify-inputs (package-propagated-inputs python-requests)
+       (replace "python-charset-normalizer" python-charset-normalizer-2.10)
+       (replace "python-urllib3" python-urllib3-1.26)))))
+
+(define-public openconnect-sso
+  (package
+    (name "openconnect-sso")
+    ;; 0.8.0 was released in 2021, the latest update on master HEAD is from
+    ;; 2023.
+    (properties '((commit . "94128073ef49acb3bad84a2ae19fdef926ab7bdf")
+                  (revision . "0")))
+    (version (git-version "0.8.0"
+                          (assoc-ref properties 'revision)
+                          (assoc-ref properties 'commit)))
+    (source
+      (origin
+        (method git-fetch)
+        (uri (git-reference
+               (url "https://github.com/vlaci/openconnect-sso")
+              (commit (assoc-ref properties 'commit))))
+        (file-name (git-file-name name version))
+        (sha256
+         (base32 "08cqd40p9vld1liyl6qrsdrilzc709scyfghfzmmja3m1m7nym94"))))
+    (build-system pyproject-build-system)
+    (arguments
+     `(#:phases
+       (modify-phases %standard-phases
+          (add-after 'unpack 'use-poetry-core
+            (lambda _
+              ;; Patch to use the core poetry API.
+              (substitute* "pyproject.toml"
+                (("poetry.masonry.api")
+                 "poetry.core.masonry.api"))))
+         (add-after 'unpack 'patch-openconnect
+           (lambda* (#:key inputs #:allow-other-keys)
+             (substitute* "openconnect_sso/app.py"
+               (("\"openconnect\"")
+                (string-append "\""
+                               (search-input-file inputs "/sbin/openconnect")
+                               "\""))))))))
+    (inputs
+     (list openconnect
+           python-attrs
+           python-colorama
+           python-keyring
+           python-lxml-4.9
+           python-prompt-toolkit
+           python-pyotp
+           python-pyqt-6
+           python-pyqtwebengine-6
+           python-pysocks
+           python-pyxdg
+           python-requests
+           python-structlog
+           python-toml))
+    (native-inputs
+     (list python-poetry-core
+           python-pytest
+           python-pytest-asyncio
+           python-pytest-httpserver))
+    (home-page "https://github.com/vlaci/openconnect-sso")
+    (synopsis "OpenConnect wrapper script supporting Azure AD (SAMLv2)")
+    (description
+     "This package provides a wrapper script for OpenConnect supporting Azure AD
+(SAMLv2) authentication to Cisco SSL-VPNs.")
+    (license license:gpl3)))
+
+;; Login to the UTHSC VPN fails with an SSLV3_ALERT_HANDSHAKE_FAILURE
+;; on newer python-requests.
+(define openconnect-sso-uthsc
+  (package
+    (inherit openconnect-sso)
+    (name "openconnect-sso-uthsc")
+    (inputs
+     (modify-inputs (package-inputs openconnect-sso)
+       (replace "python-requests" python-requests-2.28)))))
+
 (define uthsc-vpn
   (with-imported-modules '((guix build utils))
     #~(begin
@@ -34,8 +174,10 @@
                                  ("system_default" . "system_default_sect"))
                                 ("system_default_sect"
                                  ("Options" . "UnsafeLegacyRenegotiation")))))
-        (invoke #$(file-append openconnect-sso "/bin/openconnect-sso")
-                "--server" "uthscvpn1.uthsc.edu"
+        (setenv "REQUESTS_CA_BUNDLE"
+                #$(local-file "uthsc-certificate.pem"))
+        (invoke #$(file-append openconnect-sso-uthsc "/bin/openconnect-sso")
+                "--server" "$vpn-server" ; ask us for end-point or see UT docs
                 "--authgroup" "UTHSC"
                 "--"
                 "--script" (string-join (cons #$(file-append vpn-slice "/bin/vpn-slice")