Production Deployment#

This guide covers deploying NexusLIMS-CDCS to a production environment with proper security, SSL certificates, and data management.

Prerequisites#

Required Knowledge#

  • Linux system administration

  • Docker and Docker Compose

  • Basic understanding of DNS and SSL/TLS certificates

  • Basic PostgreSQL database administration

  • Web server/reverse proxy concepts

Required Access#

  • Root or sudo access to production server

  • DNS management for your domain

  • (Optional) CIFS/SMB or NFS for data files

  • (Optional) SMTP server for application notifications


System Requirements#

Minimum Hardware#

Resource

Minimum

Recommended

CPU

4 cores

8 cores

RAM

8 GB

16 GB

Disk

20 GB SSD

100+ GB SSD

Network

100 Mbps

1 Gbps

Software Requirements#

Software

Version

OS

Ubuntu 22.04 LTS or RHEL 8+

Docker

24.0+

Docker Compose

2.20+

Git

2.0+

Network Requirements#

  • Firewall: Ports 80 and 443 open (for ACME certificate validation)

  • DNS: Ability to create A records pointing to your server

  • Optional: Firewall rules for database ports if external access needed


Pre-Deployment Checklist#

Before deploying, ensure you have:

  • Production server provisioned and accessible via SSH

  • Domain name(s) registered and DNS configured

  • SSL certificate strategy determined (either self-managed certs or automatic ACME/Let’s Encrypt)

  • Data storage locations identified and accessible

  • Backup strategy planned

  • Strong passwords generated for database and Redis

  • Django secret key generated (minimum 50 characters)

  • (Optional) SMTP server configured for email notifications


Domain and DNS Setup#

Required DNS Records#

You need two subdomains (replace nexuslims.example.com with your domain):

  1. Main application: nexuslims.example.com

  2. File server: files.nexuslims.example.com

DNS Configuration#

Create A records pointing to your server’s public IP:

nexuslims.example.com.       A    203.0.113.42
files.nexuslims.example.com. A    203.0.113.42

Verify DNS Propagation#

Before deployment, verify DNS is working:

dig nexuslims.example.com
dig files.nexuslims.example.com

Both should return your server’s IP address.


SSL Certificate Options#

Option 2: Manual Certificates#

Best for: Organizations with existing PKI or certificate vendors

  1. Obtain certificates from your CA (you need fullchain.pem and privkey.pem)

  2. Create certificate directory and copy certificates:

    mkdir -p /opt/nexuslims/certs
    chmod 700 /opt/nexuslims/certs
    cp fullchain.pem /opt/nexuslims/certs/
    cp privkey.pem /opt/nexuslims/certs/
    chmod 600 /opt/nexuslims/certs/*
    
  3. Set the certificate path in your .env file:

    CADDY_CERTS_HOST_PATH=/opt/nexuslims/certs
    

    The docker-compose.prod.yml already includes a volume mount that uses this variable:

    - ${CADDY_CERTS_HOST_PATH}:/etc/caddy/certs:ro
    
  4. Update caddy/Caddyfile.prod to use the certificates:

    https://{$DOMAIN} {
        tls /etc/caddy/certs/fullchain.pem /etc/caddy/certs/privkey.pem
        # ... rest of config
    }
    
    https://{$FILES_DOMAIN} {
        tls /etc/caddy/certs/fullchain.pem /etc/caddy/certs/privkey.pem
        # ... rest of config
    }
    

Note

When using manual certificates, you are responsible for renewing them before expiration.


Environment Configuration#

1. Clone Repository#

sudo mkdir -p /opt/nexuslims
cd /opt/nexuslims
git clone https://github.com/datasophos/NexusLIMS-CDCS.git
cd NexusLIMS-CDCS/deployment

2. Create Environment File#

cp .env.prod.example .env
chmod 600 .env

3. Configure Environment Variables#

Edit .env with your production values. See Configuration for detailed documentation of all variables.

Critical variables to set:

# Domain configuration
DOMAIN=nexuslims.example.com
FILES_DOMAIN=files.nexuslims.example.com

# Security (generate strong values!)
SECRET_KEY=your-50-character-minimum-secret-key
POSTGRES_PASS=strong-database-password
REDIS_PASS=strong-redis-password

# ACME certificate email
CADDY_ACME_EMAIL=admin@example.com

# File paths (adjust to your storage locations)
NX_DATA_HOST_PATH=/mnt/nexuslims/data
NX_INSTRUMENT_DATA_HOST_PATH=/mnt/nexuslims/instrument-data
NX_CDCS_BACKUPS_HOST_PATH=/opt/nexuslims/backups

Warning

Never commit .env to version control! It contains secrets.


File Storage Setup#

NexusLIMS-CDCS serves two types of files:

  • NexusLIMS data: Thumbnail images, metadata files

  • Instrument data: Raw microscopy data files

Option 1: Local Storage#

sudo mkdir -p /mnt/nexuslims/data
sudo mkdir -p /mnt/nexuslims/instrument-data
sudo chown -R $USER:$USER /mnt/nexuslims
sudo chmod -R 755 /mnt/nexuslims

Option 2: NFS Mount#

# Install NFS client
sudo apt-get install nfs-common  # Ubuntu/Debian

# Create mount points
sudo mkdir -p /mnt/nexuslims/data
sudo mkdir -p /mnt/nexuslims/instrument-data

# Mount NFS shares
sudo mount -t nfs nfs-server:/export/nexuslims/data /mnt/nexuslims/data
sudo mount -t nfs nfs-server:/export/nexuslims/instrument-data /mnt/nexuslims/instrument-data

# Add to /etc/fstab for persistence
echo "nfs-server:/export/nexuslims/data /mnt/nexuslims/data nfs defaults 0 0" | sudo tee -a /etc/fstab
echo "nfs-server:/export/nexuslims/instrument-data /mnt/nexuslims/instrument-data nfs defaults 0 0" | sudo tee -a /etc/fstab

Option 3: CIFS/SMB Mount#

# Install CIFS utilities
sudo apt-get install cifs-utils  # Ubuntu/Debian

# Create credentials file
sudo mkdir -p /etc/smbcredentials
sudo touch /etc/smbcredentials/nexuslims.cred
sudo chmod 600 /etc/smbcredentials/nexuslims.cred

sudo tee /etc/smbcredentials/nexuslims.cred > /dev/null << EOF
username=your_smb_username
password=your_smb_password
domain=WORKGROUP
EOF

# Create mount points and mount
sudo mkdir -p /mnt/nexuslims/data
sudo mkdir -p /mnt/nexuslims/instrument-data

sudo mount -t cifs //nas-server/nexuslims-data /mnt/nexuslims/data \
  -o credentials=/etc/smbcredentials/nexuslims.cred,uid=$(id -u),gid=$(id -g)

sudo mount -t cifs //nas-server/nexuslims-instrument-data /mnt/nexuslims/instrument-data \
  -o credentials=/etc/smbcredentials/nexuslims.cred,uid=$(id -u),gid=$(id -g)

Deployment#

1. Load Admin Commands#

cd /opt/nexuslims/NexusLIMS-CDCS/deployment
source admin-commands.sh

This provides the dc-prod alias (shortcut for docker compose -f docker-compose.base.yml -f docker-compose.prod.yml).

2. Build and Pull Images#

dc-prod build  # Build CDCS and Caddy images
dc-prod pull   # Pull PostgreSQL and Redis images

3. Start Services#

dc-prod up -d

4. Monitor Startup#

# Watch logs
dc-prod logs -f

# Check service status
dc-prod ps

All services should show Up and healthy.

5. Verify Certificate Acquisition#

If using ACME, watch Caddy logs for certificate issuance:

dc-prod logs -f caddy

You should see: certificate obtained successfully


Post-Deployment Setup#

1. Run Initialization Script#

The initialization script sets up the superuser, schema, and XSLT stylesheets:

admin-init

This will:

  1. Verify database migrations are complete

  2. Create superuser (prompts for credentials)

  3. Upload NexusLIMS schema

  4. Load and configure XSLT stylesheets

  5. Load data exporters

2. Verify Installation#

admin-stats

Expected output:

============================================================
NexusLIMS-CDCS System Statistics
============================================================

Users:
  Total:      1
  Superusers: 1

Templates:
  Total: 1
    - Nexus Experiment Schema (Version 1)

XSLT Stylesheets:
  Total: 2
    - detail_stylesheet.xsl
    - list_stylesheet.xsl

============================================================

3. Test File Serving#

Navigate to:

  • https://files.nexuslims.example.com/data/

  • https://files.nexuslims.example.com/instrument-data/

Both should show directory listings.

4. Create Backup Directory#

sudo mkdir -p /opt/nexuslims/backups
sudo chown $USER:$USER /opt/nexuslims/backups

Important

The backup directory must be owned by the user running Docker, not root. Otherwise backup scripts will fail with permission errors.

Restart services to pick up the mount:

dc-prod down
dc-prod up -d

5. Configure Automated Backups#

Create a daily backup script:

cat > /opt/nexuslims-backup.sh << 'EOF'
#!/bin/bash
set -e

cd /opt/nexuslims/NexusLIMS-CDCS/deployment
source admin-commands.sh

# Run backup
admin-backup

# Also create a database dump
admin-db-dump

# Remove backups older than 30 days
find /opt/nexuslims/backups -type d -name "backup_*" -mtime +30 -exec rm -rf {} \; 2>/dev/null || true
find /opt/nexuslims/backups -type f -name "backup_*.sql" -mtime +30 -delete 2>/dev/null || true
EOF

chmod +x /opt/nexuslims-backup.sh

# Add to crontab (daily at 2 AM)
(crontab -l 2>/dev/null; echo "0 2 * * * /opt/nexuslims-backup.sh") | crontab -

Security Hardening#

Firewall Configuration#

UFW (Ubuntu):

sudo ufw allow 22/tcp   # SSH
sudo ufw allow 80/tcp   # HTTP (for ACME)
sudo ufw allow 443/tcp  # HTTPS
sudo ufw enable

firewalld (RHEL):

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload

Database Security#

  • Don’t expose PostgreSQL port externally unless necessary

  • Use strong passwords (already set in .env)

  • Keep PostgreSQL container updated

Container Security#

Keep images updated regularly:

dc-prod pull
dc-prod up -d

Log Rotation#

Configure Docker log rotation in /etc/docker/daemon.json:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

Restart Docker: sudo systemctl restart docker


Upgrading#

Application Updates#

Important

Releases that include a schema change require one extra step after rebuild: admin-upgrade-schema adds the new schema version to the database, migrates all existing records to it, and refreshes the XSLT stylesheets. It is safe to run on every upgrade — if nothing changed, it exits cleanly.

  1. Backup current deployment:

    source admin-commands.sh
    admin-backup
    
  2. Pull latest code and check out the release tag:

    cd /opt/nexuslims/NexusLIMS-CDCS
    git fetch
    git checkout v3.21.0-nx1  # or desired version (downgrades are not supported)
    
  3. Review the changelog for breaking changes and new environment variables

  4. Apply any new environment variables from .env.prod.example to your .env file

  5. Rebuild and restart:

    cd deployment
    source admin-commands.sh
    dc-prod build
    dc-prod down
    dc-prod up -d
    
  6. Run Django database migrations:

    docker exec nexuslims_prod_cdcs python manage.py migrate
    
  7. Upgrade schema, records, and XSLT:

    source admin-commands.sh
    admin-upgrade-schema
    

    Expected output when a schema change is present:

    Step 1/3: Schema check
      → On-disk hash differs from active version — adding Version 2
      ✓ Added Version 2 to "Nexus Experiment Schema"
      ✓ Version 2 set as current
    
    Step 2/3: Record migration
      ✓ Migrated 42 record(s) to current schema version
    
    Step 3/3: XSLT update
      ✓ detail_stylesheet.xsl updated
      ✓ list_stylesheet.xsl updated
    
    Schema upgrade complete.
    

    Expected output when nothing changed:

    Step 1/3: Schema check
      ✓ Active version hash matches on-disk schema — no upgrade needed
    
    Step 2/3: Record migration
      ✓ All records already on current version — no migration needed
    
    Step 3/3: XSLT update
      ✓ detail_stylesheet.xsl updated
      ✓ list_stylesheet.xsl updated
    
    Nothing to upgrade.
    
  8. Verify:

    admin-stats
    

Manual upgrade via web UI (fallback)#

Use this path if the script does not work or you want to verify each step interactively.

Step 1 — Upload new schema version

Navigate to https://<your-domain>/staff-core-admin/templates, click on the “Versions” button for “Nexus Experiment Schema”, then click “Upload New Version” button to upload the updated nexus-experiment.xsd from deployment/schemas/.

Step 2 — Migrate records

After uploading the version, you will be redirected to https://<your-domain>/staff-core-admin/template/data-migration to migrate records from the previous version to the one you just uploaded. It should be safe to just click “Migrate” without anything else

  • 1. Source template: select the old version(s)

  • 2. Select data: select all existing records

  • 3. XSLT: leave blank — no XML transformation is needed for purely additive schema changes

  • 4. Target template: Choose the latest version, which should be what you just uploaded

  • Click Migrate and wait for the progress bar to complete

Step 3 — Set new version as current

Navigate back to https://<your-domain>/staff-core-admin/templates, click on the “Versions” button for “Nexus Experiment Schema”, then click “Set Current” on the latest version you just uploaded.

Update XSLT

This is most easily done from the admin script. Run:

cd deployment
source admin-commands.sh
admin-update-xslt

Rollback#

If upgrade fails:

dc-prod down
git checkout v3.20.0-nx0  # or whichever version matches your NexusLIMS backend — see {ref}`compatibility`
cd deployment
dc-prod build
dc-prod up -d

If database schema changed, restore from backup:

admin-db-restore /opt/nexuslims/backups/backup_20260109_120000.sql

Next Steps#