This guide explains how to build and deploy DXLander using Docker.
# Pull and run from GitHub Container Registry
docker run -d \
-p 3000:3000 \
-p 3001:3001 \
-v dxlander-data:/app/.dxlander \
--name dxlander \
ghcr.io/dxlander/dxlander:latest
# Access at http://localhost:3000# Download docker-compose.yml
curl -O https://raw.githubusercontent.com/dxlander/dxlander/main/docker-compose.yml
# Start DXLander
docker compose up -d
# View logs
docker compose logs -f
# Stop DXLander
docker compose down
# Stop and remove data
docker compose down -v# Build for your platform
docker build -t dxlander:local .
# Build for multiple platforms (requires buildx)
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t dxlander:local .docker run -d \
-p 3000:3000 \
-p 3001:3001 \
-v dxlander-data:/app/.dxlander \
--name dxlander \
dxlander:local| Variable | Default | Description |
|---|---|---|
NODE_ENV |
production |
Node environment |
PORT |
3000 |
Web UI port |
API_PORT |
3001 |
API server port |
DXLANDER_HOME |
/app/.dxlander |
Data directory |
DXLANDER_ENCRYPTION_KEY |
Auto-generated | Master encryption key (44+ chars base64) |
Named Volume (Recommended for Production)
docker run -v dxlander-data:/app/.dxlander ghcr.io/dxlander/dxlander:latestBind Mount (Easier Access to Data)
docker run -v ~/.dxlander:/app/.dxlander ghcr.io/dxlander/dxlander:latestInside the container:
- Database:
/app/.dxlander/data/dxlander.db - Projects:
/app/.dxlander/projects/ - Encryption Key:
/app/.dxlander/encryption.key - Logs:
/app/.dxlander/logs/
docker run -d \
-p 8080:3000 \
-p 8081:3001 \
-e PORT=3000 \
-e API_PORT=3001 \
ghcr.io/dxlander/dxlander:latestFor production deployments, you can provide a custom encryption key via the DXLANDER_ENCRYPTION_KEY environment variable instead of relying on the auto-generated key file.
# Generate a secure 44+ character base64 key (32 raw bytes encoded in base64 produce 44 characters)
ENCRYPTION_KEY=$(openssl rand -base64 32)
# Run with custom key (no key file will be generated)
docker run -d \
-p 3000:3000 \
-p 3001:3001 \
-e DXLANDER_ENCRYPTION_KEY=$ENCRYPTION_KEY \
-v dxlander-data:/app/.dxlander \
ghcr.io/dxlander/dxlander:latestBenefits of using DXLANDER_ENCRYPTION_KEY:
- Works better in distributed deployments where multiple instances need to share the same key
- More suitable for container environments where file-based keys don't persist well
- Complies with organizational security requirements that mandate external key management
- Simplifies backup/restore scenarios by ensuring the same key is used across environments
Security Requirements:
- The key must be at least 44 characters long (32 raw bytes encoded in base64)
- Keep the key secure and backed up
- For maximum security, use Docker secrets or Kubernetes secrets to manage the key
The image includes a health check that polls http://localhost:3000 every 30 seconds.
# Check container health
docker inspect --format='{{.State.Health.Status}}' dxlander
# View health check logs
docker inspect --format='{{json .State.Health}}' dxlander | jqversion: '3.8'
services:
dxlander:
image: ghcr.io/dxlander/dxlander:latest
ports:
- '3000:3000'
- '3001:3001'
volumes:
- dxlander-data:/app/.dxlander
restart: unless-stopped
volumes:
dxlander-data:version: '3.8'
services:
dxlander:
image: ghcr.io/dxlander/dxlander:latest
ports:
- '8080:3000'
- '8081:3001'
environment:
- NODE_ENV=production
- PORT=3000
- API_PORT=3001
- DXLANDER_ENCRYPTION_KEY=${ENCRYPTION_KEY}
volumes:
- ./data:/app/.dxlander
restart: unless-stopped
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:3000']
interval: 30s
timeout: 10s
retries: 3version: '3.8'
services:
dxlander:
image: ghcr.io/dxlander/dxlander:latest
expose:
- '3000'
- '3001'
volumes:
- dxlander-data:/app/.dxlander
restart: unless-stopped
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.dxlander.rule=Host(`dxlander.example.com`)'
- 'traefik.http.services.dxlander.loadbalancer.server.port=3000'
volumes:
dxlander-data:# Backup volume to tar archive
docker run --rm \
-v dxlander-data:/data \
-v $(pwd):/backup \
alpine tar czf /backup/dxlander-backup-$(date +%Y%m%d).tar.gz -C /data .# Restore from backup
docker run --rm \
-v dxlander-data:/data \
-v $(pwd):/backup \
alpine tar xzf /backup/dxlander-backup-YYYYMMDD.tar.gz -C /data# Using Docker
docker pull ghcr.io/dxlander/dxlander:latest
docker stop dxlander
docker rm dxlander
docker run -d \
-p 3000:3000 \
-p 3001:3001 \
-v dxlander-data:/app/.dxlander \
--name dxlander \
ghcr.io/dxlander/dxlander:latest
# Using Docker Compose
docker compose pull
docker compose up -d# Using Docker
docker logs -f dxlander
# Using Docker Compose
docker compose logs -f
# Last 100 lines
docker logs --tail 100 dxlander# Check logs
docker logs dxlander
# Check health status
docker inspect dxlander | grep -A 5 Health
# Verify ports are not in use
lsof -i :3000
lsof -i :3001# Fix volume permissions
docker run --rm \
-v dxlander-data:/data \
alpine chown -R 1001:1001 /data# Stop and remove container
docker stop dxlander
docker rm dxlander
# Remove volume (WARNING: Deletes all data)
docker volume rm dxlander-data
# Start fresh
docker run -d \
-p 3000:3000 \
-p 3001:3001 \
-v dxlander-data:/app/.dxlander \
--name dxlander \
ghcr.io/dxlander/dxlander:latestThe Docker image uses a multi-stage build that mirrors the npm production build:
- deps: Install all dependencies
- builder: Build workspace packages and bundle API with esbuild
- api-prod: Deploy API with production dependencies using pnpm
- runner: Final image with Next.js standalone + bundled API
This ensures:
- ✅ Small final image size
- ✅ Native modules work correctly (better-sqlite3)
- ✅ Standalone Next.js with embedded dependencies
- ✅ Production-optimized bundles
- Container runs as non-root user
dxlander(UID 1001) - Encryption key auto-generated on first run (44+ character base64 minimum enforced)
- For production, set
DXLANDER_ENCRYPTION_KEYexplicitly - Use Docker secrets for sensitive environment variables
- Keep volumes backed up regularly
- Documentation: documentation/
- Issues: GitHub Issues
- Discussions: GitHub Discussions