Skip to content

Web Server Setup

This guide covers setting up the web server layer including Nginx configuration, SSL certificates, and UI deployment.

Prerequisites

Before starting, ensure you have completed the Installation steps.

1. Nginx Configuration

Configure Nginx to serve both the UI and API:

upstream app_server {
  # fail_timeout=0 means we always retry an upstream even if it failed
  # to return a good HTTP response (in case the Unicorn master nukes a
  # single worker for timing out).
  server unix:/run/gunicorn.socket fail_timeout=0;
}

server {
    listen 443 ssl;
    server_name your-labid.com;

    root /opt/labid-ui/dist;  # UI static files
    index index.html;

    ssl_certificate /path/to/your/certificate.crt;
    ssl_certificate_key /path/to/your/private.key;

    # API requests go to Django
    location ~ ^/(admin|api|api-auth|metrics) {
        proxy_pass http://app_server;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Static files from Django
    location ^~ /static/ {
        alias /opt/labid/labid/static_collection/;
    }

    location ^~ /attachments/ {
        alias /opt/labid/labid/storage/attachments/;
    }

    # SPA routing for UI
    location / {
        try_files $uri $uri/ /index.html;
    }
}

Configuration Examples

Example Nginx configurations are available in the repository:

2. SSL Certificate Setup

Install SSL certificate using Let's Encrypt:

# Install certbot
sudo apt install certbot python3-certbot-nginx

# Obtain and install certificate
sudo certbot --nginx -d your-labid.com

# Verify automatic renewal
sudo certbot renew --dry-run

Manual Certificate Installation

If using a custom certificate:

# Copy certificate files
sudo cp your-certificate.crt /etc/ssl/certs/labid.crt
sudo cp your-private-key.key /etc/ssl/private/labid.key

# Set appropriate permissions
sudo chmod 644 /etc/ssl/certs/labid.crt
sudo chmod 600 /etc/ssl/private/labid.key

3. UI Deployment

The LabID UI is a Vue.js single-page application (SPA). It is compiled into static files that Nginx serves directly — there is no running Node.js process in production.

Step 1 — Install Node.js via nvm

nvm (Node Version Manager) lets you install and switch Node versions without sudo or system-package conflicts.

# Install nvm (run as the labid user or your own user)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash

# Restart your shell or source the init file, then:
nvm install --lts
nvm use --lts

Step 2 — Clone the repository and install dependencies

cd /opt
sudo git clone https://gitlab.com/lab-integrated-data/labid-ui.git labid-ui
sudo chown -R labid:labid labid-ui

sudo -u labid -i
cd /opt/labid-ui
git checkout production
npm install
# Do NOT run npm run build yet — configure the environment first (Step 3)

Step 3 — Configure .env.local before building

Environment variables are baked in at compile time

The UI is a compiled SPA. Values in .env.local are embedded into the JavaScript bundle during npm run build. If you build before setting the correct URL, the wrong URL is hard-coded into production — changing .env.local afterwards has no effect until you rebuild.

# Still as the labid user inside /opt/labid-ui
nano .env

Update the following variables in .env:

# Full public API URL — must include /api/v2
VITE_API_URL=https://your-labid.com/api/v2

# Public base URL of your LabID instance
VITE_DEPLOYMENT_URL=https://your-labid.com

Use https:// — not http:// and not the internal hostname

If your site is served over TLS (https://) but VITE_API_URL contains an http:// URL or refers to an internal hostname and port (e.g. http://bs-myserver.ethz.ch:8000), browsers will block the API calls as mixed active content. The result is the "Server is down" banner on the home page, even though the backend itself is running fine.

Always set VITE_API_URL to the public https:// domain that users access — not the internal gunicorn address.

For the full list of available options, see the UI repository .env file.

Step 4 — Build the UI

npm run build
# Outputs static files to /opt/labid-ui/dist/
# Nginx serves this directory directly (see Section 1 above)

npm run dev and npm run preview are development-only tools

npm run dev (port 5173) and npm run preview (port 4173) start temporary development servers. They are not a production deployment method — do not proxy Nginx to either of these ports. Production always uses the static files in dist/ served directly by Nginx.

Reference: The docker-compose configuration shows the complete integration between UI (Vue.js), backend (Django), and nginx routing.

4. LibreOffice Online (Optional)

For document editing capabilities, set up LibreOffice Online:

# Using Docker (recommended)
docker run -t -d -p 9980:9980 \
  -e "extra_params=--o:ssl.enable=false" \
  -e "username=admin" \
  -e "password=secure_password" \
  -e "domain=your-labid\\.com" \
  --name=lool \
  collabora/code

Configure LibreOffice Integration

Update your environment configuration:

# Add to /opt/labid/labid/.env
echo "DJANGO_LOOL_INSTANCE=http://localhost:9980" >> /opt/labid/labid/.env
echo "DJANGO_LOOL_WOPI_ADDRESS=https://your-labid.com" >> /opt/labid/labid/.env

Nginx Configuration for LibreOffice

Add LibreOffice routes to your Nginx configuration. See the docker-compose/web/default.conf for routing examples.

5. Testing Web Server Setup

Test Nginx Configuration

# Test Nginx configuration syntax
sudo nginx -t

# Reload Nginx if configuration is valid
sudo systemctl reload nginx

Verify Web Access

  1. Backend API: https://your-labid.com/api/v2/version/
  2. Admin Interface: https://your-labid.com/admin/
  3. UI Application: https://your-labid.com/

Check SSL Certificate

# Verify SSL certificate
openssl s_client -connect your-labid.com:443 -servername your-labid.com

6. Security Hardening

Nginx Security Headers

Add security headers to your Nginx configuration:

# Security headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Content-Security-Policy "default-src 'self'";

Firewall Configuration

# Allow only necessary ports
sudo ufw allow 22    # SSH
sudo ufw allow 80    # HTTP
sudo ufw allow 443   # HTTPS
sudo ufw enable

Next Steps