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:
- An isolated network (no internet)
- Physical media (USB drives, DVDs) for software transfer
- A trusted workstation for building images
- Pre-downloaded container images
Prerequisites
Offline Environment
-
Network isolation: Verify no routes to the internet
# From the isolated network, these should fail:
ping 8.8.8.8
curl https://google.com -
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 -
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)
- Workstation with Docker, Helm, and internet access
- 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
-
Copy USB drive to air-gapped server
-
Mount the USB:
mount /dev/sdb1 /mnt/usb -
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):
- Extract the ZIP file
- Chrome/Edge: Go to
chrome://extensions/→ Enable Developer Mode → Load unpacked → Select the folder - Firefox: Go to
about:addons→ Install from File → Select the XPI
Step 7: Manual Updates
Update Procedure
When new versions are available:
-
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 -
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
- Network isolation: Verify at firewall level that no traffic routes externally
- Physical security: Restrict access to the deployment servers
- Credential management: Use strong, randomly-generated secrets for all services
- Audit logging: Enable all audit logs to immutable storage
- Regular testing: Test backup restoration quarterly
- 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.