Infrastructure and deployment configuration for CDS Connect services.
| Repository | Description |
|---|---|
| iupui-soic/AHRQ-CDS-Connect-CQL-SERVICES | CQL Services (fork) |
| iupui-soic/AHRQ-CDS-Connect-Authoring-Tool | Authoring Tool (fork) |
┌───────────────────────────────────────────────────────────┐
│ cdsconnect.org (nginx) │
│ Let's Encrypt SSL/TLS │
└──────────────┬────────────────┬────────────────┬──────────┘
│ │ │
┌───────────▼───────┐ ┌──────▼──────┐ ┌───────▼──────────┐
│ /api/library │ │/cds-services│ │ /authoring │
│ / │ │ │ │ /authoring/api │
└─────────┬─────────┘ └──────┬──────┘ └───────┬──────────┘
│ │ │
┌─────────▼──────────────────▼─────┐ ┌───────▼─────────┐
│ CQL Services │ │ Authoring Tool │
│ (ghcr.io/iupui-soic/...) │ │ API + Frontend │
└──────────────────────────────────┘ └──────┬──────────┘
│
┌─────────────────┼─────────────┐
│ │ │
┌─────▼─────┐ ┌──────▼─────┐ ┌─────▼──────┐
│ MongoDB │ │ CQL-to-ELM │ │ VSAC Cache │
└───────────┘ └────────────┘ └────────────┘
# On your production server (Ubuntu/Debian with Docker installed)
git clone https://github.com/iupui-soic/cds-connect-deployment.git
cd cds-connect-deployment
# Copy environment variables template
cp .env.example .envnano .envRequired changes:
# 1. Set your domain
DOMAIN=yourdomain.org
LETSENCRYPT_EMAIL=admin@yourdomain.org
# 2. Generate and set a session secret (run this command first, then paste result)
# openssl rand -hex 32
AUTH_SESSION_SECRET=paste-the-64-character-hex-string-here
# 3. Set your UMLS API key (get from https://uts.nlm.nih.gov/uts/)
UMLS_API_KEY=your-actual-umls-keyImportant: The
.envfile does NOT execute shell commands. You must runopenssl rand -hex 32separately and paste the resulting value.
# Copy the example users file
cp config/local-users.example.json config/local-users.json
# Generate a bcrypt hash for your password (using Docker)
docker run --rm node:18-alpine sh -c \
'cd /tmp && npm init -y >/dev/null 2>&1 && npm install bcryptjs >/dev/null 2>&1 && node -e "require(\"bcryptjs\").hash(\"YOUR_PASSWORD_HERE\", 10).then(console.log)"'Edit config/local-users.json with the generated hash:
nano config/local-users.json{
"admin": "$2b$10$paste-your-bcrypt-hash-here"
}Security Note: Never commit
local-users.jsonto version control. It's already in.gitignore.
If using a different domain, update the nginx configuration:
# Edit the SSL config
nano nginx/conf.d/cdsconnect.conf
# Replace all occurrences of 'cdsconnect.org' with your domain
# Lines to change: server_name and ssl_certificate pathsAlso update the init config template:
nano nginx/conf.d/cdsconnect-init.conf.example
# Replace 'cdsconnect.org' with your domain# Make script executable
chmod +x init-ssl.sh
# Ensure your domain's DNS points to this server, then run:
./init-ssl.shThe script will:
- Temporarily start nginx for the ACME challenge
- Obtain certificates from Let's Encrypt
- Configure nginx with SSL and start all services
# Check all services are running
docker compose ps
# Test the endpoints
curl -k https://localhost/health
curl -k https://localhost/cds-services
curl -k https://localhost/authoring/api/config
# View logs if needed
docker compose logs -fYour services are now available at:
- CQL Services: https://yourdomain.org/cds-services
- Authoring Tool: https://yourdomain.org/authoring
# Create directories if they don't exist
mkdir -p config/libraries config/hooks
# Copy your CQL libraries (ELM JSON files)
cp -r /path/to/your/libraries/* config/libraries/
# Copy your hook configurations
cp -r /path/to/your/hooks/* config/hooks/
# Restart CQL Services to pick up new files
docker compose restart cql-servicesUsers are defined in config/local-users.json. Each entry is a username with a bcrypt-hashed password.
Option 1: Using Node.js (requires bcryptjs)
node -e "require('bcryptjs').hash('mypassword', 10).then(console.log)"Option 2: Using Docker (no local Node.js required)
docker run --rm node:18-alpine sh -c \
"npm install -g bcryptjs && node -e \"require('bcryptjs').hash('mypassword', 10).then(console.log)\""Option 3: Using an online bcrypt generator
- Use cost factor/rounds: 10
- Example sites: bcrypt-generator.com, browserling.com/tools/bcrypt
{
"admin": "$2b$10$N9qo8uLOickgx2ZMRZoMye1234567890abcdefghij",
"alice": "$2b$10$abcdefghij1234567890NOqo8uLOickgx2ZMRZoMye",
"bob": "$2b$10$1234567890abcdefghijN9qo8uLOickgx2ZMRZoMye"
}- Generate a bcrypt hash for the new user's password
- Add the username and hash to
config/local-users.json - Restart the authoring tool (or it will pick up changes on next login attempt):
docker-compose restart authoring-tool
- Generate a new bcrypt hash for the new password
- Update the hash in
config/local-users.json - Restart the authoring tool:
docker-compose restart authoring-tool
- Delete the user's line from
config/local-users.json - Restart the authoring tool
cds-connect-deployment/
├── docker-compose.yml # Service orchestration
├── .env.example # Environment variables template
├── .env # Your config (DO NOT COMMIT)
├── deploy.sh # Deployment helper script
├── init-ssl.sh # SSL certificate setup
├── README.md # This file
├── nginx/
│ ├── nginx.conf # Main nginx config
│ └── conf.d/
│ ├── cdsconnect.conf # Site config with SSL
│ └── cdsconnect-init.conf.example # Template for initial SSL setup
├── config/
│ ├── libraries/ # Your CQL/ELM libraries
│ ├── hooks/ # Your CDS Hooks configurations
│ ├── local-users.example.json # Example users file (template)
│ └── local-users.json # Your users (DO NOT COMMIT)
└── .github/workflows/
├── docker-build-cql-services.yml # CI workflow for CQL Services
└── docker-build-authoring-tool.yml # CI workflow for Authoring Tool
-
Copy the workflow file to your fork:
cp .github/workflows/docker-build-cql-services.yml \ /path/to/AHRQ-CDS-Connect-CQL-SERVICES/.github/workflows/docker-build.yml
-
Enable GitHub Actions in repository settings
-
Push to trigger a build, or manually trigger via Actions tab
-
Copy the workflow file to your fork:
cp .github/workflows/docker-build-authoring-tool.yml \ /path/to/AHRQ-CDS-Connect-Authoring-Tool/.github/workflows/docker-build.yml
-
Adjust Dockerfile paths based on repository structure
-
Enable GitHub Actions and push to trigger build
By default, ghcr.io images are private. To make them public:
- Go to:
https://github.com/orgs/iupui-soic/packages - Click on each package
- Go to Package settings → Change visibility → Public
| Variable | Required | Description |
|---|---|---|
DOMAIN |
Yes | Your domain (e.g., cdsconnect.org) |
LETSENCRYPT_EMAIL |
Yes | Email for Let's Encrypt notifications |
UMLS_API_KEY |
Yes | UMLS API key for VSAC downloads |
AUTH_SESSION_SECRET |
Yes | Secret for session encryption (min 32 chars) |
AUTH_LOCAL_ACTIVE |
No | Enable local authentication (default: true) |
CQL_SERVICES_VERSION |
No | Image tag (default: latest) |
AUTHORING_TOOL_VERSION |
No | Image tag (default: latest) |
Generate a secure session secret:
openssl rand -hex 32# Pull latest images
docker-compose pull
# Restart with new images
docker-compose up -d# Edit .env
CQL_SERVICES_VERSION=v3.2.0
AUTHORING_TOOL_VERSION=v2.1.0
# Redeploy
docker-compose up -d# All services
docker-compose logs -f
# Specific service
docker-compose logs -f cql-services
docker-compose logs -f authoring-api# Create backup
docker-compose exec mongodb mongodump --out /data/db/backup
# Copy to host
docker cp cds-mongodb:/data/db/backup ./mongodb-backup-$(date +%Y%m%d)Certificates auto-renew via certbot container. To force renewal:
docker-compose run --rm certbot renew --force-renewal
docker-compose restart nginx# Service status
docker-compose ps
# Health checks
curl -k https://localhost/health
curl -k https://localhost/cds-services
curl -k https://localhost/authoring/api/config# Check logs
docker-compose logs cql-services
# Check if image exists
docker images | grep cql-services
# Rebuild if needed
docker-compose build --no-cache cql-services# Test certificate
openssl s_client -connect cdsconnect.org:443 -servername cdsconnect.org
# Check certbot logs
docker-compose logs certbot
# Re-run SSL setup
./init-ssl.sh- Never commit
.envorlocal-users.json- Contains secrets and password hashes - Use bcrypt for passwords - Never store plain text passwords in
local-users.json - Use specific image tags in production - Not
latest - Enable HSTS - Uncomment in nginx config after SSL confirmed working
- MongoDB authentication - Consider enabling for production
- Firewall - Only expose ports 80/443 externally
- Secrets rotation - Periodically rotate
AUTH_SESSION_SECRET - Strong passwords - Require strong passwords for all users