Tutorial: Setup with k3d and nginx
This tutorial shows you how to set up Loculus on a plain Linux server that is reachable through a domain. For a local instance (i.e., an instance that is not publicly accessible), please check out this tutorial.
We will use k3d to run a Kubernetes cluster within Docker, nginx as a proxy server and Let’s Encrypt to obtain an SSL certificate. The tutorial was tested on an Ubuntu 24.04.
Prerequisites
- A Linux server (e.g. Ubuntu 24.04) with ideally at least 4 CPUs and 8 GB RAM
- A domain
- Install Docker: see this guide
- Install k3d: see this guide
- Install kubectl: see this guide
- Install Helm: see this guide
- Install nginx: see this guide
Step 1: Create a k3d cluster
We prepared a script that creates a k3d cluster, waits until the Traefik proxy in the cluster is ready and contains relevant port mappings. Create a file create-cluster.sh
with the following content, make it executable with chmod +x create-cluster.sh
and execute it with ./create-cluster.sh
.
#!/bin/bash
set -euo pipefail
cluster_name="loculusCluster"
website_port_mapping="127.0.0.1:9000:30081@agent:0"backend_port_mapping="127.0.0.1:9079:30082@agent:0"lapis_port_mapping="127.0.0.1:9080:80@loadbalancer"database_port_mapping="127.0.0.1:9432:30432@agent:0"keycloak_port_mapping="127.0.0.1:9083:30083@agent:0"
if k3d cluster list | grep -q "$cluster_name"; then echo "Cluster $cluster_name already exists, skipping creation" exit 0fi
echo "Creating cluster $cluster_name"k3d cluster create "$cluster_name" \ --port "$website_port_mapping" \ --port "$backend_port_mapping" \ --port "$lapis_port_mapping" \ --port "$database_port_mapping" \ --port "$keycloak_port_mapping" \ --agents 1
is_traefik_running() { namespace="kube-system" label="app.kubernetes.io/name=traefik" result=$(kubectl get pods -n "$namespace" -l "$label" --no-headers 2>&1) if ! kubectl get pods -n "$namespace" -l "$label" --no-headers 2>&1; then echo "Error executing kubectl: $result" return 1 fi
if echo "$result" | grep -q "Running"; then return 0 else return 1 fi}
while ! is_traefik_running; do echo "Waiting for Traefik to start..." sleep 5done
echo "Traefik is running."
Step 2: Install secret generator
The Loculus Helm chart uses the Mittwald secrets generator. You can install it with:
helm repo add mittwald https://helm.mittwald.dehelm repo updatehelm upgrade --install kubernetes-secret-generator mittwald/kubernetes-secret-generator --set secretLength=32 --set watchNamespace=""
Step 3: Configure the instance
Create a file my-values.yaml
with the following configuration for the instance. Please enter your domain name (e.g., loculus.example.com
), define an initial admin password and give your instance a name. This instance uses a cluster-internal test database and a very simple organism. We will improve the configuration later.
name: '<your instance name>'host: '<your domain>'environment: local # Do not change this: this setup uses a "local" environment as the public traffic is proxied through nginx.
runDevelopmentMainDatabase: truerunDevelopmentKeycloakDatabase: true
website: runtimeConfig: public: backendUrl: 'https://api.<your domain>/backend' lapisUrlTemplate: 'https://api.<your domain>/%organism%' keycloakUrl: 'https://auth.<your domain>'
auth: verifyEmail: false resetPasswordAllowed: false registrationAllowed: true smtp: null identityProviders: null
disableIngest: truedisableEnaSubmission: true
seqSets: enabled: false
secrets: keycloak-admin: type: raw data: initialAdminPassword: '<your initial admin password>'
organisms: angelovirus: schema: organismName: 'Angelovirus' metadata: - name: country type: string initiallyVisible: true - name: city type: string initiallyVisible: true website: tableColumns: - country - city defaultOrder: descending defaultOrderBy: country preprocessing: - version: 1 image: ghcr.io/loculus-project/preprocessing-nextclade args: - 'prepro' configFile: log_level: DEBUG genes: [] batch_size: 100 referenceGenomes: nucleotideSequences: - name: 'main' sequence: 'NNN' # We are not performing alignment here, so this sequence doesn't matter genes: []
Step 4: Install and deploy
Clone the Loculus repository. The Loculus Helm chart is located in kubernetes/loculus/
and you can install it with
helm install -f my-values.yaml my-loculus loculus/kubernetes/loculus
Wait a few moments until everything gets started. You can see the status of the pods with:
kubectl get pods
Once the pods are running, the website is locally available (i.e., from within the sever) at port 9000 and you can download the HTML of the landing page with:
curl localhost:9000
In the output, you should see somewhere:
<title>Home | <Your instance name></title>
Step 5: Configure DNS / domain
You have to add DNS entries of your domain to point to the server. Please add DNS entries for the following domains and sub-domains:
<your domain>
api.<your domain>
auth.<your domain>
Step 6: Set up nginx
Now, you can use nginx as a reverse proxy to make the instance publicly accessible through the domain.
First, delete the default nginx page with:
sudo rm /etc/nginx/sites-enabled/default
Next, create the config for Loculus at /etc/nginx/sites-enabled/loculus
with the following content and replace <your domain>
:
server { listen 80;
server_name <your domain>;
location / { proxy_pass http://127.0.0.1:9000/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }}
server { listen 80;
server_name auth.<your domain>;
location / { proxy_pass http://127.0.0.1:9083/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_buffer_size 16k; proxy_buffers 4 16k; proxy_busy_buffers_size 32k; }}
server { listen 80;
server_name api.<your domain>;
location /backend/ { proxy_pass http://127.0.0.1:9079/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }
location / { proxy_pass http://127.0.0.1:9080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }}
Reload nginx with
sudo service nginx reload
You should now be able to see the Loculus website at http://<your domain>
. Many links and features don’t work yet as they require SSL which we describe in the next step. For example, if you click on “Login”, you should see an error.
Step 7: Set up SSL certificates
You can use certbot to obtain SSL certificates from Let’s Encrypt. To install it, run:
sudo apt install certbot python3-certbot-nginx
Now, you can request the certificates with the following command:
sudo certbot --nginx -d <your domain> -d api.<your domain> -d auth.<your domain>
You can now go back to your website at <your domain>
and should be able to create an account and use the instance!
Step 8: Use a dedicated database (important for production systems)
If you want to use the instance in production, it is important to use a database outside the Loculus Helm chart. The database that the Helm chart starts is purely for test purposes and might be reset during an update.
Loculus uses PostgreSQL as its database management system and requires two databases: one for the Loculus backend, and one for the Keycloak server. Please check out the official documentation for details on how to install PostgreSQL if you don’t have one already.
Then, adapt my-values.yaml
and add under the secrets
key:
secrets: database: type: raw data: url: 'jdbc:postgresql://<Loculus database host>:<Loculus database port>/<Loculus database name>' username: '<Loculus database user>' password: '<Loculus database password>' keycloak-database: type: raw data: addr: '<Keycloak database host>' port: '<Keycloak database port>' database: '<Keycloak database name>' username: '<Keycloak database user>' password: '<Keycloak database password>'
To reinstall Loculus, execute:
helm uninstall my-loculushelm install -f my-values.yaml my-loculus loculus/kubernetes/loculus
Final words
Hopefully, you now have a working Loculus instance and can start adapting it to the organisms of your needs. Please check out the Helm chart config reference for information on the available configurations.