(cdcs-local-test-deployment)=
# Local Test Deployment

This guide explains how to set up a local production-like environment with HTTPS for testing production deployment procedures before deploying to a real server. The instructions assume you have adminstrator rights for the system, and (on MacOS and Linux) use directories such as `/opt` for system data. It also requires admin access to modify your system's `hosts` file. You may be able to run most the setup without admin privileges by changing all the directories to sub-folders of `${HOME}`, but that is as-of-yet untested.

## Overview

Testing production configurations locally helps you:
- Validate your `.env` settings before deployment
- Test backup and restore procedures
- Verify SSL certificate configuration
- Practice upgrade and rollback procedures
- Debug issues in a safe environment

## Prerequisites

- Docker Desktop or Docker Engine with Compose
- For MacOS, [`homebrew`](https://brew.sh/) installed and available
- For Windows, [`scoop`](https://scoop.sh/) installed and available
- `sudo` or administrator access to edit system files such as `/etc/hosts`
- Optionally, `git` and `jq` installed (can be installed with `homebrew` or `scoop`)
- Folders for your read-only instrument data (`NX_INSTRUMENT_DATA_HOST_PATH`) and NexusLIMS data (preview images, metadata files, etc. in `NX_DATA_HOST_PATH`). These can be network mounted folders, if desired.

**Note**: These instructions were developed primarily for a MacOS or Linux host, but the general approach should work on Windows

---

## Setup Steps

### 0. Obtain NexusLIMS-CDCS deployment files

Create a folder to store NexusLIMS files, then clone the NexusLIMS-CDCS repository to get the deployment configuration files:

`````{tab-set}

````{tab-item} macOS (zsh/bash)
```bash
sudo mkdir -p /opt/nexuslims
sudo chown ${USER}:staff /opt/nexuslims

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

````{tab-item} Linux (zsh/bash)
```bash
sudo mkdir -p /opt/nexuslims
sudo chown ${USER}:${USER} /opt/nexuslims

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

````{tab-item} Windows (Powershell)
Run PowerShell as Administrator:
```powershell
# Create folder
New-Item -ItemType Directory -Force -Path C:\nexuslims
# Set ownership of that folder to current user
icacls C:\nexuslims /grant "${env:USERNAME}:(OI)(CI)F" /T
```

Run Powershell as normal user:
```powershell
cd C:\nexuslims
git clone https://github.com/datasophos/NexusLIMS-CDCS.git
cd NexusLIMS-CDCS\deployment
```
````

`````

**Note**: The deployment files are located in the `deployment/` directory within the root of the NexusLIMS-CDCS repository.

**Alternative to git clone**: If you don't have git installed or prefer not to use it, you can download the repository as a ZIP file from GitHub:

1. Download from: [https://github.com/datasophos/NexusLIMS-CDCS/archive/refs/heads/main.zip](https://github.com/datasophos/NexusLIMS-CDCS/archive/refs/heads/main.zip)
2. Extract the ZIP file to your nexuslims directory (`/opt/nexuslims` on macOS/Linux, `C:\nexuslims` on Windows)
3. Rename the extracted folder from `NexusLIMS-CDCS-main` to `NexusLIMS-CDCS`

### 0.5. Provide Docker access to NexusLIMS directory (macOS only)

On macOS, Docker Desktop needs explicit permission to access directories outside of your home folder. Since we're using `/opt/nexuslims`, you need to add it to Docker's file sharing settings:

1. Open **Docker Desktop**
2. Click the **Settings** (gear) icon in the top-right
3. Navigate to **Resources** → **File sharing**
4. Enter `/opt/nexuslims` or browse to select it in the list of folders
5. Click the **+** button to add it to Docker's allowed folders
6. Click **Apply & restart**

Docker will restart and mount the `/opt/nexuslims` directory, making it available to containers.

**Note**: If you see permission errors when running containers later, verify that this directory appears in the File sharing list.


### 1. Install mkcert

[`mkcert`](https://github.com/FiloSottile/mkcert) allows you to create locally-trusted certificates so the full HTTPS installation will work as if you had obtained a certificate from a trusted certificate authority.

`````{tab-set}

````{tab-item} macOS (zsh/bash)
```bash
brew install mkcert
brew install nss  # Firefox support
mkcert -install
```
````

````{tab-item} Linux (zsh/bash)
```bash
# Ubuntu/Debian
sudo apt install libnss3-tools

# Download mkcert from https://github.com/FiloSottile/mkcert/releases
# or follow other instructions from https://github.com/FiloSottile/mkcert
sudo mv mkcert-v*-linux-amd64 /usr/local/bin/mkcert
sudo chmod +x /usr/local/bin/mkcert
mkcert -install
```
````

````{tab-item} Windows (Powershell)
Install `mkcert` via `scoop`

```powershell
scoop bucket add extras
scoop install mkcert
mkcert -install
```
````

`````

### 2. Generate Certificates

Create certificates for your test domain:

`````{tab-set}

````{tab-item} macOS/Linux (zsh/bash)
```bash
mkdir -p /opt/nexuslims/local-certs

cd /opt/nexuslims/local-certs
mkcert \
  "nexuslims-local.test" \
  "files.nexuslims-local.test" \
  "localhost" \
  "127.0.0.1" \
  "::1"

# Rename to standard certificate names expected by NexusLIMS
mv nexuslims-local.test+4.pem fullchain.pem
mv nexuslims-local.test+4-key.pem privkey.pem
```
````

````{tab-item} Windows (Powershell)
```powershell
New-Item -ItemType Directory -Force -Path C:\nexuslims\local-certs

cd C:\nexuslims\local-certs
mkcert `
  "nexuslims-local.test" `
  "files.nexuslims-local.test" `
  "localhost" `
  "127.0.0.1" `
  "::1"

# Rename to standard certificate names expected by NexusLIMS
Move-Item nexuslims-local.test+4.pem fullchain.pem
Move-Item nexuslims-local.test+4-key.pem privkey.pem
```
````

`````

This creates a local certificate and key that will be trusted by your system as if it were issued by a trusted certificate authority:

- `fullchain.pem` (certificate)
- `privkey.pem` (private key)

**Note**: NexusLIMS expects standard certificate names (`fullchain.pem` and `privkey.pem`), which is why we rename the mkcert-generated files.

### 3. Update your system's hosts file

Redirect the test domains to localhost:

`````{tab-set}

````{tab-item} macOS/Linux (zsh/bash)
```bash
sudo tee -a /etc/hosts << EOF
127.0.0.1 nexuslims-local.test
127.0.0.1 files.nexuslims-local.test
EOF
```

To verify the content:
```bash
cat /etc/hosts | grep nexus

# should return:
# 127.0.0.1 nexuslims-local.test
# 127.0.0.1 files.nexuslims-local.test
```
````

````{tab-item} Windows (Powershell)
Run PowerShell as Administrator, then add the entries:
```powershell
Add-Content -Path C:\Windows\System32\drivers\etc\hosts -Value @"
127.0.0.1 nexuslims-local.test
127.0.0.1 files.nexuslims-local.test
"@
```

To verify the content:
```powershell
Get-Content C:\Windows\System32\drivers\etc\hosts | Select-String nexus

# should return:
# 127.0.0.1 nexuslims-local.test
# 127.0.0.1 files.nexuslims-local.test
```
````

`````

### 4. Create Backup Directory

*Note*: this is not strictly necessary, but if you want to use the backup capabilities built into the NexusLIMS deployment helper scripts, this directory should be created:

`````{tab-set}

````{tab-item} macOS/Linux (zsh/bash)
```bash
mkdir -p /opt/nexuslims/backups
```
````

````{tab-item} Windows (Powershell)
```powershell
New-Item -ItemType Directory -Force -Path C:\nexuslims\backups
```
````

`````

### 5. Configure Environment

This section explains how to set values that will enable NexusLIMS-CDCS to work on your local machine:

```````{tab-set}

``````{tab-item} macOS/Linux (zsh/bash)

Change to the deployment folder (if you're not already there) and copy the example `.env.prod.example` file
into a new file named `.env`:

```bash
cd /opt/nexuslims/NexusLIMS-CDCS/deployment
cp .env.prod.example .env
```

Configure the `.env` file for local testing:

`````{tab-set}

````{tab-item} Automated (recommended)

Use sed to update the standard values automatically:

```bash
sed -i '' \
  -e 's/^DOMAIN=.*/DOMAIN=nexuslims-local.test/' \
  -e 's/^FILES_DOMAIN=.*/FILES_DOMAIN=files.nexuslims-local.test/' \
  -e 's|^# CADDY_CERTS_HOST_PATH=.*|CADDY_CERTS_HOST_PATH=/opt/nexuslims/local-certs|' \
  -e 's|^NX_CDCS_BACKUPS_HOST_PATH=.*|NX_CDCS_BACKUPS_HOST_PATH=/opt/nexuslims/backups|' \
  .env
```

Then manually edit `.env` to set your data paths and API token (these values are at the top of the file):

```bash
# File paths (adjust these to match your actual data locations)
NX_DATA_HOST_PATH=/mnt/nexuslims/data
NX_INSTRUMENT_DATA_HOST_PATH=/mnt/nexuslims/instrument-data

# Admin API token (generate a secure random token)
# This is used by the NexusLIMS backend to authenticate API requests
NX_ADMIN_API_TOKEN=<generate-with-command-below>
```

Generate a secure API token:

```bash
python3 -c "from secrets import token_urlsafe; print(token_urlsafe(30))"
```

Copy the output and paste it as the value for `NX_ADMIN_API_TOKEN` in your `.env` file.
````

````{tab-item} Manual

Edit `.env` with any text editor and set these values:

```bash
# Domain configuration
DOMAIN=nexuslims-local.test
FILES_DOMAIN=files.nexuslims-local.test

# Enable custom certificates
CADDY_CERTS_HOST_PATH=/opt/nexuslims/local-certs

# Backup directory
NX_CDCS_BACKUPS_HOST_PATH=/opt/nexuslims/backups

# Admin API token (generate with: python3 -c "from secrets import token_urlsafe; print(token_urlsafe(30))")
NX_ADMIN_API_TOKEN=<paste-generated-token-here>

# File paths (adjust to match your actual data locations)
NX_DATA_HOST_PATH=/mnt/nexuslims/data
NX_INSTRUMENT_DATA_HOST_PATH=/mnt/nexuslims/instrument-data
```

````

``````

``````{tab-item} Windows (Powershell)

Change to the deployment folder (if you're not already there) and copy the example `.env.prod.example` file
into a new file named `.env`:

```powershell
cd C:\nexuslims\NexusLIMS-CDCS\deployment
Copy-Item .env.prod.example .env
```

Configure the `.env` file for local testing:

`````{tab-set}

````{tab-item} Automated (recommended)

Use PowerShell to update the standard values automatically:

```powershell
# Update standard local test values
(Get-Content .env) -replace '^DOMAIN=.*', 'DOMAIN=nexuslims-local.test' | Set-Content .env
(Get-Content .env) -replace '^FILES_DOMAIN=.*', 'FILES_DOMAIN=files.nexuslims-local.test' | Set-Content .env
(Get-Content .env) -replace '^# CADDY_CERTS_HOST_PATH=.*', 'CADDY_CERTS_HOST_PATH=C:\nexuslims\local-certs' | Set-Content .env
(Get-Content .env) -replace '^NX_CDCS_BACKUPS_HOST_PATH=.*', 'NX_CDCS_BACKUPS_HOST_PATH=C:\nexuslims\backups' | Set-Content .env
```

Then manually edit `.env` to set your data paths and API token (these values are at the top of the file):

```powershell
# File paths (adjust these to match your actual data locations)
NX_DATA_HOST_PATH=C:\nexuslims\data
NX_INSTRUMENT_DATA_HOST_PATH=C:\nexuslims\instrument-data

# Admin API token (generate a secure random token)
# This is used by the NexusLIMS backend to authenticate API requests
NX_ADMIN_API_TOKEN=<generate-with-command-below>
```

Generate a secure API token:

```powershell
python -c "from secrets import token_urlsafe; print(token_urlsafe(30))"
```

Copy the output and paste it as the value for `NX_ADMIN_API_TOKEN` in your `.env` file.

````

````{tab-item} Manual

Edit `.env` with any text editor and set these values:

```powershell
# Domain configuration
DOMAIN=nexuslims-local.test
FILES_DOMAIN=files.nexuslims-local.test

# Enable custom certificates
CADDY_CERTS_HOST_PATH=C:\nexuslims\local-certs

# Backup directory
NX_CDCS_BACKUPS_HOST_PATH=C:\nexuslims\backups

# Admin API token (generate with: python -c "from secrets import token_urlsafe; print(token_urlsafe(30))")
NX_ADMIN_API_TOKEN=<paste-generated-token-here>

# File paths (adjust to match your actual data locations)
NX_DATA_HOST_PATH=C:\nexuslims\data
NX_INSTRUMENT_DATA_HOST_PATH=C:\nexuslims\instrument-data
```

````

`````
```````

(start-services)=
### 6. Start Services

NexusLIMS-CDCS includes helper scripts that provide some useful shortcuts for interacting with the docker deployment stack, including `admin-commands.sh`/`admin-commands.ps1`. These need to be sourced once to load the functions into your current session, and you will need to "re-source" the file again if you close your terminal session.

For example, one command made available is `dc-prod`, which is a shortcut/alias for running `docker compose -f docker-compose.base.yml -f docker-compose.prod.yml`, so you don't have to type those filenames out everytime you want to run a command:

`````{tab-set}

````{tab-item} macOS/Linux (zsh/bash)
From the `NexusLIMS-CDCS/deployment` folder, run the following to load the helper functions into your session:
```bash
source admin-commands.sh
```

Then you can run the following commands to bring up the docker deployment stack and initialize the CDCS application. This will take a few moments the first time, since it needs to build some containers locally,
but subsequent runs will be much faster:

```bash
dc-prod up -d   # Brings up the docker deployment stack and "detaches" to run it in the background
admin-init      # Sets up the CDCS superuser, Nexus Experiment schema, XSLT translations, etc.
```
````

````{tab-item} Windows (Powershell)
From the `NexusLIMS-CDCS/deployment` folder, run the following to load the helper functions into your session:
```powershell
. .\admin-commands.ps1
```

**Note**: The dot-space-dot syntax (`. .\admin-commands.ps1`) is PowerShell's equivalent to bash's `source` command. It loads the functions into your current session.

Then you can run the following commands to bring up the docker deployment stack and initialize the CDCS application:

```powershell
dc-prod up -d   # Brings up the docker deployment stack and "detaches" to run it in the background
admin-init      # Sets up the CDCS superuser, Nexus Experiment schema, XSLT translations, etc.
```
````

`````

### 7. Test NexusLIMS-CDCS Access

The NexusLIMS-CDCS application should now be up and running! 

**Access the application:**
- **Main application**: [https://nexuslims-local.test](https://nexuslims-local.test)
- **File server (NexusLIMS metadata/image previews)**: [https://files.nexuslims-local.test/data](https://files.nexuslims-local.test/data)
- **File server (instrument data)**: [https://files.nexuslims-local.test/instrument-data](https://files.nexuslims-local.test/instrument-data)

Your browser should trust the certificates since they were generated by `mkcert` and installed in your system's certificate store.

If needed, you can monitor the container logs with:

```bash
dc-prod logs --follow
```

### 8. Test API Access

Verify that the CDCS REST API is accessible and returning data:

`````{tab-set}

````{tab-item} macOS/Linux

The following tests require [`jq`](https://jqlang.org/) to be installed. If not
already, install with `brew install jq` (macOS) or your system package manager (Linux).

In each of the commands below, replace `YOUR_API_TOKEN` with the value from `NX_ADMIN_API_TOKEN` 
in your `.env` file

```bash
# Test basic API connectivity (should reply with "Authentication credentials were not provided.")
curl -s https://nexuslims-local.test/rest/data/ | jq '.'

# Get template information (content field truncated for readability)
curl -s https://nexuslims-local.test/rest/template/ \
  -H "Authorization: Token YOUR_API_TOKEN" | \
  jq '.[] | .content = (.content[:100] + "... [truncated]")'

# Query for all data records (requires authentication, should return empty for new install)
curl -s https://nexuslims-local.test/rest/data/ \
  -H "Authorization: Token YOUR_API_TOKEN" | jq '.'
```
````

````{tab-item} Windows

The following tests require [`jq`](https://jqlang.org/) to be installed. If not
already, install with `scoop install jq`.

In each of the commands below, replace `YOUR_API_TOKEN` with the value from `NX_ADMIN_API_TOKEN` 
in your `.env` file

```powershell
# Test basic API connectivity (should reply with "Authentication credentials were not provided.")
curl -s https://nexuslims-local.test/rest/data/ | jq '.'

# Get template information (content field truncated for readability)
# Replace YOUR_API_TOKEN with the value from NX_ADMIN_API_TOKEN in your .env file
curl -s https://nexuslims-local.test/rest/template/ `
  -H "Authorization: Token YOUR_API_TOKEN" | `
  jq '.[] | .content = (.content[:100] + \"... [truncated]\")'

# Query for all data records (requires authentication, should return empty for new install)
curl -s https://nexuslims-local.test/rest/data/ `
  -H "Authorization: Token YOUR_API_TOKEN" | jq '.'
```

**Note**: PowerShell requires escaping quotes in JSON strings with backslashes.
````

`````

If the API is working correctly, you should see JSON responses with available endpoints and data,
and the backend should be able to communicate successfully with the CDCS frontend.

(backend-mkcert-ca)=
### Configure the NexusLIMS backend to trust the mkcert CA

The steps above make `nexuslims-local.test` trusted by your **browser** and command-line tools
like `curl`, because `mkcert -install` adds the mkcert CA to your OS certificate store.
However, the NexusLIMS **backend** (the record builder) uses Python's `requests` library,
which does *not* consult the OS trust store — it only trusts certificates in the `certifi`
bundle. You must explicitly tell it about the mkcert CA, or the backend will fail with an
`SSLCertVerificationError` when trying to upload records to CDCS.

Find the mkcert CA certificate path:

`````{tab-set}

````{tab-item} macOS/Linux (zsh/bash)
```bash
# Print the mkcert CA directory
mkcert -CAROOT
# The CA certificate is at: $(mkcert -CAROOT)/rootCA.pem
# e.g. /Users/myuser/Library/Application Support/mkcert/rootCA.pem
```
````

````{tab-item} Windows (Powershell)
```powershell
# Print the mkcert CA directory
mkcert -CAROOT
# The CA certificate is at: <output>\rootCA.pem
# e.g. C:\Users\myuser\AppData\Local\mkcert\rootCA.pem
```
````

`````

Then add the following to the NexusLIMS **backend** `.env` file (not the CDCS deployment `.env`):

```
NX_CERT_BUNDLE_FILE=/path/to/mkcert/rootCA.pem
```

Replace the path with the actual location printed by `mkcert -CAROOT`, appending `/rootCA.pem`.

---

## Testing Production Procedures

With this setup, you can now test all production procedures from the {doc}`production` guide. These processes are totally optional. The application should be up and running for local testing at this point.

### Test Backups

`````{tab-set}

````{tab-item} macOS/Linux (zsh/bash)
```bash
source admin-commands.sh    # only required if using a new terminal window
admin-backup

# Check backup was created
ls -la /opt/nexuslims/backups/
```
````

````{tab-item} Windows (Powershell)
```powershell
. .\admin-commands.ps1      # only required if using a new terminal window
admin-backup

# Check backup was created
Get-ChildItem C:\nexuslims\backups\
```
````

`````

### Test Database Operations

`````{tab-set}

````{tab-item} macOS/Linux (zsh/bash)
```bash
source admin-commands.sh    # only required if using a new terminal window

# Create a database dump
admin-db-dump

# View statistics
admin-stats
```
````

````{tab-item} Windows (Powershell)
```powershell
. .\admin-commands.ps1      # only required if using a new terminal window

# Create a database dump
admin-db-dump

# View statistics
admin-stats
```
````

`````

### Verify SSL Certificate

`````{tab-set}

````{tab-item} macOS/Linux (zsh/bash)
```bash
# Verify certificate files exist and show their details (requires openssl)
openssl x509 -in /opt/nexuslims/local-certs/fullchain.pem -text -noout | grep -E "(Subject:|Issuer:|Not Before|Not After|DNS)"
openssl rsa -in /opt/nexuslims/local-certs/privkey.pem -check -noout

# Verify HTTPS is working with trusted certificate
curl -I https://nexuslims-local.test

# Check certificate details from the running server (requires openssl)
openssl s_client -connect nexuslims-local.test:443 \
  -servername nexuslims-local.test < /dev/null 2>/dev/null | \
  openssl x509 -noout -dates -issuer
```
````

````{tab-item} Windows (Powershell)
```powershell
# Verify certificate files exist and show their details
openssl x509 -in C:\nexuslims\local-certs\fullchain.pem -text -noout | Select-String "(Subject:|Issuer:|Not Before|Not After)"
openssl rsa -in C:\nexuslims\local-certs\privkey.pem -check -noout

# Verify HTTPS is working with trusted certificate
curl -I https://nexuslims-local.test

# Check certificate details from the running server (requires openssl)
# Note: You may need to install openssl via: scoop install openssl
openssl s_client -connect nexuslims-local.test:443 `
  -servername nexuslims-local.test 2>$null | `
  openssl x509 -noout -dates -issuer
```
````

`````

---

## Stopping the Environment

The following commands will "tear down" the docker deployment stack, effectively stopping the application. Any data stored (users, records, etc.) will be preserved for the next time you run `dc-prod up -d`.

`````{tab-set}

````{tab-item} macOS/Linux (zsh/bash)
```bash
cd deployment
source admin-commands.sh
dc-prod down
```
````

````{tab-item} Windows (Powershell)
```powershell
cd deployment
. .\admin-commands.ps1
dc-prod down
```
````

`````

---

## Cleanup

⚠️ **Warning!** ⚠️

This will delete all data in the test environment, including any users you created and records you uploaded. You will have to start from [Step 6: Start Services](start-services) again if you want to use it after cleanup.

To completely remove the local test environment:

`````{tab-set}

````{tab-item} macOS/Linux (zsh/bash)
```bash
# Stop containers and remove volumes
cd deployment
source admin-commands.sh
dc-prod down -v           # this command will delete all application data

## NOTE! Stop here and do not run the following commands if all you want
##       to do is clear application data. If you run the following commands,
##       you'll have to start at the very beginning of this guide.

# Remove certificates
sudo rm -rf /opt/nexuslims/local-certs

# Remove backups
sudo rm -rf /opt/nexuslims/backups

# Remove /etc/hosts entries (creating /etc/hosts.bak with current content)
sudo sed -i.bak '/nexuslims-local.test/d' /etc/hosts
```
````

````{tab-item} Windows (Powershell)
Run PowerShell as Administrator:
```powershell
# Stop containers and remove volumes
cd C:\nexuslims\NexusLIMS-CDCS\deployment
. .\admin-commands.ps1
dc-prod down -v           # this command will delete all application data

## NOTE! Stop here and do not run the following commands if all you want
##       to do is clear application data. If you run the following commands,
##       you'll have to start at the very beginning of this guide.

# Remove certificates
Remove-Item -Recurse -Force C:\nexuslims\local-certs

# Remove backups
Remove-Item -Recurse -Force C:\nexuslims\backups

# Remove /etc/hosts entries (this command needs to be run as an administrator)
$hostsPath = "C:\Windows\System32\drivers\etc\hosts"
(Get-Content $hostsPath) | Where-Object { $_ -notmatch 'nexuslims-local.test' } | Set-Content $hostsPath
```
````

`````

---

## Differences from Production

This local test environment differs from real production in these ways:

| Aspect | Local Test | Production |
|--------|-----------|------------|
| **Certificates** | mkcert (local CA) | Let's Encrypt (ACME) |
| **Domains** | `.test` TLD | Real domains |
| **DNS** | `/etc/hosts` | Real DNS |
| **Data** | Test data or empty | Production data |
| **Passwords** | Simple test values | Strong passwords |

---

## Troubleshooting

### Certificate Not Trusted

If browsers show "not secure":

```bash
# Reinstall mkcert CA
mkcert -install

# Restart browser
```

### Cannot Access URLs

Check `/etc/hosts`:
```bash
cat /etc/hosts | grep nexuslims
```

Should show:
```
127.0.0.1 nexuslims-local.test
127.0.0.1 files.nexuslims-local.test
```

### Backend SSL error when uploading records

If the record builder fails with `SSLCertVerificationError` / `certificate verify failed: unable
to get local issuer certificate` when trying to reach `nexuslims-local.test`, the backend's
Python `requests` library does not trust the mkcert CA. This is distinct from browser trust —
`mkcert -install` only affects the OS/browser store, not Python. See the
[Configure the NexusLIMS backend to trust the mkcert CA](#backend-mkcert-ca)
section above and set `NX_CERT_BUNDLE_FILE` in the backend `.env` to the mkcert `rootCA.pem`.

### Port Already in Use

If ports 80/443 are in use:
```bash
# Find what's using the ports
sudo lsof -i :80
sudo lsof -i :443

# Stop the conflicting service
```

---

## Next Steps

Once your local HTTPS test environment is working:

1. Walk through each section of the {doc}`production` guide
2. Test backup and restore procedures
3. Practice upgrade and rollback workflows
4. Document any issues for your production deployment
