Skip to main content

Air-Gapped Deployment

Deploy Noxys in fully isolated networks with no external internet access—ideal for governments, militaries, and critical infrastructure.

Overview

Air-gapped Noxys deployments run entirely on-premise with zero external dependencies:

  • No cloud services
  • No internet connectivity
  • No telemetry or external API calls
  • Manual software updates
  • All dependencies bundled locally

This guide assumes you have:

  1. An isolated network (no internet)
  2. Physical media (USB drives, DVDs) for software transfer
  3. A trusted workstation for building images
  4. Pre-downloaded container images

Prerequisites

Offline Environment

  1. Network isolation: Verify no routes to the internet

    # From the isolated network, these should fail:
    ping 8.8.8.8
    curl https://google.com
  2. Air-gapped registry: A private Docker/container registry on the isolated network

    # Example: Harbor, Docker Registry, or Quay Enterprise
    # Must be accessible from deployment servers
  3. Offline package mirrors (optional but recommended):

    • Ubuntu/Debian: apt-mirror or apt-cacher-ng
    • pip: pip-mirror or PyPI mirror
    • npm: npm registry mirror

Prepare Workstation (Connected)

  1. Workstation with Docker, Helm, and internet access
  2. USB drive(s) large enough to hold:
    • Container images (3-5 GB)
    • Helm charts and manifests
    • Installation scripts

Step 1: Prepare Container Images

On your connected workstation:

Pull Images

# Noxys images
docker pull noxys/proxy:latest
docker pull noxys/console:latest

# Database & infrastructure
docker pull postgres:16
docker pull redis:7
docker pull nats:latest
docker pull nginx:latest

# If using Kubernetes
docker pull kube-apiserver:latest
docker pull coredns:latest
docker pull etcd:latest

Save as Tarballs

# Create archive directory
mkdir -p /mnt/usb/images

# Save images
docker save noxys/proxy:latest | gzip > /mnt/usb/images/noxys-proxy.tar.gz
docker save noxys/console:latest | gzip > /mnt/usb/images/noxys-console.tar.gz
docker save postgres:16 | gzip > /mnt/usb/images/postgres.tar.gz
docker save redis:7 | gzip > /mnt/usb/images/redis.tar.gz
docker save nats:latest | gzip > /mnt/usb/images/nats.tar.gz

Step 2: Prepare Configuration & Scripts

Create Installation Bundle

On your workstation:

mkdir -p /mnt/usb/noxys-airgap
cd /mnt/usb/noxys-airgap

# Copy Docker Compose files
cp docker-compose.yml .
cp docker-compose.prod.yml .

# Create environment template
cat > .env.example << 'EOF'
NOXYS_ENV=production
NOXYS_PORT=8080
NOXYS_JWT_SECRET=<generate-with-openssl-rand-base64-32>
NOXYS_DB_URL=postgres://noxys:PASSWORD@postgres:5432/noxys
NOXYS_REDIS_URL=redis://redis:6379/0
NOXYS_NATS_URL=nats://nats:4222
NOXYS_CORS_ALLOWED_ORIGINS=https://noxys.internal.gov
NOXYS_ALLOWED_DOMAINS=internal.gov,agency.gov
NOXYS_LOG_LEVEL=info

# Disable all external integrations
NOXYS_SLACK_WEBHOOK_URL=
NOXYS_SENDGRID_API_KEY=
NOXYS_STRIPE_API_KEY=
OTEL_ENABLED=false

POSTGRES_USER=noxys
POSTGRES_PASSWORD=<generate-secure-password>
POSTGRES_DB=noxys
EOF

# Create setup script
cat > setup.sh << 'EOF'
#!/bin/bash
set -euo pipefail

echo "Loading container images from archive..."
cd /path/to/images
docker load -i noxys-proxy.tar.gz
docker load -i noxys-console.tar.gz
docker load -i postgres.tar.gz
docker load -i redis.tar.gz
docker load -i nats.tar.gz

echo "Starting Noxys..."
cd /path/to/noxys-airgap
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

echo "Waiting for services to be ready..."
sleep 10

# Run health checks
docker compose exec api curl -f http://localhost:8080/healthz || exit 1
docker compose exec postgres psql -U noxys -d noxys -c "SELECT 1" || exit 1

echo "Noxys is running!"
echo "Access the dashboard at: https://noxys.internal.gov"
EOF

chmod +x setup.sh

Step 3: Transfer to Air-Gapped Network

  1. Copy USB drive to air-gapped server

  2. Mount the USB:

    mount /dev/sdb1 /mnt/usb
  3. Copy files to deployment location:

    cp -r /mnt/usb/images /srv/noxys/
    cp -r /mnt/usb/noxys-airgap /srv/noxys/

Step 4: Deploy in Air-Gapped Environment

Load Container Images

cd /srv/noxys/images

docker load < noxys-proxy.tar.gz
docker load < noxys-console.tar.gz
docker load < postgres.tar.gz
docker load < redis.tar.gz
docker load < nats.tar.gz

# Verify images are loaded
docker images

Configure Environment

cd /srv/noxys/noxys-airgap

# Copy template and customize
cp .env.example .env

# Edit .env with your values
nano .env
# - Set NOXYS_JWT_SECRET (generate: openssl rand -base64 32)
# - Set NOXYS_POSTGRES_PASSWORD (generate: openssl rand -base64 32)
# - Set NOXYS_CORS_ALLOWED_ORIGINS to your domain

Start Services

cd /srv/noxys/noxys-airgap

# Start all services
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# Verify health
docker compose ps
docker compose logs api

Step 5: Configure TLS

Generate Self-Signed Certificate (For Isolated Network)

mkdir -p ./certs

openssl req -x509 -newkey rsa:4096 -nodes \
-out ./certs/cert.pem \
-keyout ./certs/key.pem \
-days 3650 \
-subj "/C=XX/ST=State/L=City/O=Organization/CN=noxys.internal.gov"

# Add to docker-compose.prod.yml
docker compose restart api

Configure Reverse Proxy (Nginx)

# Install nginx
sudo apt-get install nginx

# Create config
sudo tee /etc/nginx/sites-available/noxys > /dev/null <<'EOF'
upstream noxys_api {
server localhost:8080;
}

upstream noxys_console {
server localhost:3000;
}

server {
listen 443 ssl http2;
server_name noxys.internal.gov;

ssl_certificate /srv/noxys/noxys-airgap/certs/cert.pem;
ssl_certificate_key /srv/noxys/noxys-airgap/certs/key.pem;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;

# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;

location /api/ {
proxy_pass http://noxys_api;
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 https;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

location / {
proxy_pass http://noxys_console;
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 https;
}
}

server {
listen 80;
server_name noxys.internal.gov;
return 301 https://$server_name$request_uri;
}
EOF

# Enable and start
sudo ln -s /etc/nginx/sites-available/noxys /etc/nginx/sites-enabled/
sudo systemctl enable nginx
sudo systemctl restart nginx

Step 6: Offline Extension Distribution

Build Extension for Air-Gapped Network

On your workstation, build the browser extension with the air-gapped backend URL:

# Clone extension source
git clone https://github.com/noxys-io/noxys-extension.git
cd noxys-extension

# Create config for air-gapped network
cat > src/config.local.ts << 'EOF'
export const API_URL = 'https://noxys.internal.gov/api';
export const BACKEND_URL = 'https://noxys.internal.gov';
EOF

# Build for Chrome, Firefox
npm install
npm run build:chrome
npm run build:firefox

# Package as ZIP
zip -r noxys-extension-airgap.zip dist/

# Transfer to air-gapped network via USB
cp noxys-extension-airgap.zip /mnt/usb/

Manual Installation in Users' Browsers

On user machines (air-gapped network):

  1. Extract the ZIP file
  2. Chrome/Edge: Go to chrome://extensions/ → Enable Developer Mode → Load unpacked → Select the folder
  3. Firefox: Go to about:addons → Install from File → Select the XPI

Step 7: Manual Updates

Update Procedure

When new versions are available:

  1. On your connected workstation:

    docker pull noxys/proxy:v0.3.0
    docker save noxys/proxy:v0.3.0 | gzip > noxys-proxy-v0.3.0.tar.gz
    # Transfer via USB to air-gapped network
  2. On the air-gapped server:

    docker load < noxys-proxy-v0.3.0.tar.gz

    # Update docker-compose.yml to reference new image
    # Then restart:
    docker compose down
    docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Version Pinning

Always pin image versions for reproducibility:

services:
api:
image: noxys/proxy:v0.3.0 # Pin version, not 'latest'
console:
image: noxys/console:v0.3.0
postgres:
image: postgres:16.2 # Pin database versions too

Step 8: Backup & Disaster Recovery

Local Backup Strategy

For air-gapped networks, backup to local storage:

# Create backup volume
mkdir -p /srv/noxys/backups

# Add to docker-compose.prod.yml
services:
postgres:
volumes:
- /srv/noxys/backups:/backups

Automated Daily Backup

#!/bin/bash
# /srv/noxys/backup.sh
BACKUP_DIR="/srv/noxys/backups"
TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S)

docker compose exec postgres pg_dump -U noxys noxys | gzip > \
"${BACKUP_DIR}/noxys_${TIMESTAMP}.sql.gz"

# Keep only 90 days of backups
find "${BACKUP_DIR}" -name "noxys_*.sql.gz" -mtime +90 -delete

echo "Backup completed: noxys_${TIMESTAMP}.sql.gz"

Schedule with Cron

crontab -e
# Add: 0 2 * * * /srv/noxys/backup.sh

Offsite Backup (Secure Transfer)

For critical environments, regularly transport backups to a secure offsite location:

# Create encrypted backup archive
tar czf noxys_backup_$(date +%Y-%m-%d).tar.gz \
/srv/noxys/backups/*.sql.gz

# Encrypt with GPG
gpg --encrypt -r your-key-id noxys_backup_*.tar.gz

# Transfer to offsite storage via secure courier

Monitoring in Air-Gapped Networks

Local Prometheus Setup

# docker-compose.prod.yml addition
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
ports:
- "9090:9090"

Health Check Script

#!/bin/bash
# /srv/noxys/health-check.sh
API_URL="https://noxys.internal.gov/api"

echo "Checking Noxys health..."

# API health
curl -k -f ${API_URL}/healthz > /dev/null && echo "✓ API: OK" || echo "✗ API: FAILED"

# Readiness
curl -k -f ${API_URL}/readyz > /dev/null && echo "✓ Dependencies: OK" || echo "✗ Dependencies: FAILED"

# Database connectivity
docker compose exec postgres psql -U noxys -d noxys -c "SELECT 1;" > /dev/null 2>&1 && \
echo "✓ Database: OK" || echo "✗ Database: FAILED"

echo "Health check completed at $(date)"

Security Considerations

  1. Network isolation: Verify at firewall level that no traffic routes externally
  2. Physical security: Restrict access to the deployment servers
  3. Credential management: Use strong, randomly-generated secrets for all services
  4. Audit logging: Enable all audit logs to immutable storage
  5. Regular testing: Test backup restoration quarterly
  6. Signed artifacts: Cryptographically sign all software transfers

Troubleshooting

Images Won't Load

# Check Docker daemon has space
docker system df

# Try loading specific image
docker load < noxys-proxy.tar.gz -v

# Check file integrity
gzip -t noxys-proxy.tar.gz

Certificate Errors

# If browser complains about self-signed cert:
# 1. Add to trusted certificates (Windows/macOS)
# 2. Or: Create cert with proper CN matching your domain

Network Connectivity Within Air-Gapped Network

# Verify docker bridge network
docker network inspect bridge

# Test connectivity between containers
docker compose exec api ping redis

Next Steps


For air-gapped deployments, contact: enterprise@noxys.eu for dedicated support.