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#

  1. Backup current deployment:

    source admin-commands.sh
    admin-backup
    
  2. Pull latest code:

    cd /opt/nexuslims/NexusLIMS-CDCS
    git fetch
    git checkout v3.19.0  # or desired version
    
  3. Review changelog for breaking changes

  4. Check for new environment variables in .env.prod.example

  5. Rebuild and restart:

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

    docker exec nexuslims_prod_cdcs python manage.py migrate
    

Rollback#

If upgrade fails:

dc-prod down
git checkout v3.18.0  # Previous version
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#