Purpose: Step-by-step guide for deploying CtxAI instances on VPS/dedicated servers
Author: Auto-generated from deployment experience
Last Updated: December 21 2025
Compatibility: Docker-capable Linux servers (AlmaLinux, CentOS, Rocky, Ubuntu, Debian)
- Prerequisites
- Docker Installation
- CtxAI Container Deployment
- Apache Reverse Proxy Configuration
- SSL/TLS Configuration
- Authentication Setup
- Domain & DNS Setup
- Verification & Testing
- Troubleshooting
- Maintenance & Updates
- Quick Reference
| Requirement | Minimum | Recommended |
|---|---|---|
| RAM | 2 GB | 4+ GB |
| Storage | 20 GB | 50+ GB |
| CPU | 1 vCPU | 2+ vCPU |
| OS | Linux (64-bit) | AlmaLinux 9, Ubuntu 22.04+ |
| Network | Static IP | Dedicated IP with reverse DNS |
- Root or sudo access to the server
- SSH access (preferably on non-standard port)
- Domain/subdomain with DNS control
- SSL certificate (Let's Encrypt or commercial)
- Docker Engine 24.0+
- Apache 2.4+ with mod_proxy, mod_proxy_http, mod_proxy_wstunnel, mod_ssl, mod_rewrite
- curl, git (optional)
Note
For detailed Docker installation instructions and alternative methods, see the Linux Installation section in the main installation guide.
# Update package index
apt-get update
# Install prerequisites
apt-get install -y ca-certificates curl gnupg
# Add Docker's official GPG key
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
# Set up repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
# Start and enable Docker
systemctl enable docker
systemctl start docker# Install required packages
dnf -y install dnf-plugins-core
# Add Docker repository (use CentOS repo for AlmaLinux/Rocky)
dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# Install Docker
dnf -y install docker-ce docker-ce-cli containerd.io docker-compose-plugin
# Start and enable Docker
systemctl enable docker
systemctl start docker
⚠️ Note: May not work on all distributions (e.g., AlmaLinux)
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
systemctl enable docker
systemctl start dockerdocker --version
docker run hello-world# Choose your installation path
CTX0_NAME="ctx0-instance" # Change this to your instance name
CTX0_PATH="/opt/${CTX0_NAME}"
# Create directories
mkdir -p ${CTX0_PATH}
mkdir -p ${CTX0_PATH}/work_dir
mkdir -p ${CTX0_PATH}/memory
mkdir -p ${CTX0_PATH}/logs# Create .env file with authentication
cat > ${CTX0_PATH}/.env << 'EOF'
# CtxAI Configuration
# Authentication (REQUIRED for web access)
AUTH_LOGIN=your_username_here
AUTH_PASSWORD=your_secure_password_here
# Optional: Additional configuration
# See CtxAI documentation for all options
EOF
⚠️ CRITICAL:AUTH_LOGINis the username, not a boolean!
- ✅ Correct:
AUTH_LOGIN=admin- ❌ Wrong:
AUTH_LOGIN=true
| Port | Use Case |
|---|---|
50080 |
Standard/recommended for reverse proxy setups |
50081, 50082... |
Additional instances on same server |
80 |
Direct access (not recommended for production) |
# Set variables
CTX0_NAME="ctx0-instance"
CTX0_PATH="/opt/${CTX0_NAME}"
CTX0_PORT="50080"
# Pull latest image
docker pull ctxos/ctxai:latest
# Run container
docker run -d --name ${CTX0_NAME} --restart unless-stopped -p ${CTX0_PORT}:80 -v ${CTX0_PATH}/.env:/ctx0/.env -v ${CTX0_PATH}/usr:/ctx0/usr ctxos/ctxai:latest# Check container is running
docker ps | grep ${CTX0_NAME}
# Check logs
docker logs ${CTX0_NAME}
# Test local access
curl -I http://127.0.0.1:${CTX0_PORT}/Expected response: HTTP/1.1 302 FOUND with Location: /login (if auth enabled)
# Debian/Ubuntu
a2enmod proxy proxy_http proxy_wstunnel ssl rewrite headers
systemctl restart apache2
# AlmaLinux/CentOS (usually pre-loaded)
httpd -M | grep -E "proxy|rewrite|ssl"Create /etc/apache2/sites-available/ctx0-instance.conf:
# CtxAI Reverse Proxy Configuration
# Instance: ctx0-instance
# Domain: a0.example.com
# HTTP - Redirect to HTTPS
<VirtualHost *:80>
ServerName a0.example.com
ServerAlias www.ctx0.example.com
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</VirtualHost>
# HTTPS - Proxy to Container
<VirtualHost *:443>
ServerName a0.example.com
ServerAlias www.ctx0.example.com
ServerAdmin webmaster@example.com
# SSL Configuration
SSLEngine on
SSLCertificateFile /path/to/certificate.crt
SSLCertificateKeyFile /path/to/private.key
SSLCertificateChainFile /path/to/chain.crt
# Proxy Configuration
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:50080/
ProxyPassReverse / http://127.0.0.1:50080/
# WebSocket Support (Required for real-time features)
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) ws://127.0.0.1:50080/$1 [P,L]
# Logging
ErrorLog ${APACHE_LOG_DIR}/ctx0-instance.error.log
CustomLog ${APACHE_LOG_DIR}/ctx0-instance.access.log combined
</VirtualHost>Enable and restart:
a2ensite ctx0-instance.conf
apache2ctl configtest
systemctl reload apache2Edit /etc/httpd/conf/extra/httpd-includes.conf:
# CtxAI Proxy Configuration
# Instance: ctx0-instance
# Domain: a0.example.com
# Note: Use specific IP, not wildcards, for DirectAdmin compatibility
<VirtualHost YOUR_SERVER_IP:80>
ServerName a0.example.com
ServerAlias www.ctx0.example.com
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</VirtualHost>
<VirtualHost YOUR_SERVER_IP:443>
ServerName a0.example.com
ServerAlias www.ctx0.example.com
ServerAdmin webmaster@example.com
SSLEngine on
# DirectAdmin SSL cert paths (adjust user and domain)
SSLCertificateFile /usr/local/directadmin/data/users/USERNAME/domains/example.com.cert.combined
SSLCertificateKeyFile /usr/local/directadmin/data/users/USERNAME/domains/example.com.key
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:50080/
ProxyPassReverse / http://127.0.0.1:50080/
# WebSocket Support
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) ws://127.0.0.1:50080/$1 [P,L]
ErrorLog /var/log/httpd/domains/ctx0.example.com.error.log
CustomLog /var/log/httpd/domains/ctx0.example.com.access.log combined
</VirtualHost>
⚠️ Important for DirectAdmin:
- Use specific IP address (e.g.,
192.168.1.100:443), not*:443- IP-bound vhosts take precedence over DirectAdmin's vhosts
- SSL certs are in
/usr/local/directadmin/data/users/USERNAME/domains/
If /etc/httpd/conf.d/ is included in your Apache config:
# Check if conf.d is included
grep 'conf.d' /etc/httpd/conf/httpd.conf
# If not, add before directadmin-vhosts.conf include:
sed -i '/Include conf\/extra\/directadmin-vhosts.conf/i Include conf.d/*.conf' /etc/httpd/conf/httpd.conf
# Create config
mkdir -p /etc/httpd/conf.d
cat > /etc/httpd/conf.d/httpd-vhosts-ctx0.conf << 'EOF'
# Your vhost config here (same as Option A)
EOF# Test configuration
httpd -t # AlmaLinux/CentOS
apachectl -t # Alternative
apache2ctl -t # Debian/Ubuntu
# Restart
systemctl restart httpd # AlmaLinux/CentOS
systemctl restart apache2 # Debian/Ubuntu# Install Certbot
# Debian/Ubuntu:
apt-get install certbot python3-certbot-apache
# AlmaLinux/CentOS:
dnf install certbot python3-certbot-apache
# Obtain certificate
certbot --apache -d a0.example.com -d www.ctx0.example.com
# Auto-renewal (usually automatic, but verify)
certbot renew --dry-runIf using DirectAdmin, SSL is typically managed automatically:
- Create domain/subdomain in DirectAdmin
- Enable "SSL" for the domain
- DirectAdmin will obtain Let's Encrypt certificate
- Certs stored in
/usr/local/directadmin/data/users/USERNAME/domains/
Place certificates in secure location:
mkdir -p /etc/ssl/ctx0
chmod 700 /etc/ssl/ctx0
# Copy your certificates
cp certificate.crt /etc/ssl/ctx0/
cp private.key /etc/ssl/ctx0/
cp chain.crt /etc/ssl/ctx0/ # if applicable
chmod 600 /etc/ssl/ctx0/*| Variable | Purpose | Example |
|---|---|---|
AUTH_LOGIN |
The username for login | AUTH_LOGIN=admin |
AUTH_PASSWORD |
The password for login | AUTH_PASSWORD=SecurePass123! |
⚠️ Common Mistake:AUTH_LOGINis the username, not a boolean to enable auth!
# Edit .env file
vi /opt/ctx0-instance/.env
# Add/update these lines:
AUTH_LOGIN=your_username
AUTH_PASSWORD=your_secure_password
# Restart container to apply
docker restart ctx0-instance- Minimum 8 characters recommended
- Special characters are supported (properly escaped)
- Avoid these characters in passwords:
' " \$ ` (or escape carefully)
To disable authentication (local/dev use only):
# Remove or comment out both lines in .env:
# AUTH_LOGIN=
# AUTH_PASSWORD=
docker restart ctx0-instanceCreate an A record pointing to your server:
| Type | Name | Value | TTL |
|---|---|---|---|
| A | a0 | YOUR_SERVER_IP | 300 |
| A | www.ctx0 | YOUR_SERVER_IP | 300 |
- Log into DirectAdmin
- Navigate to: Domain Setup → Select domain → Subdomain Management
- Create subdomain (e.g.,
ctx0) - Note: You'll override the DocumentRoot with Apache proxy config
# Check DNS resolution
dig a0.example.com +short
nslookup a0.example.com
# Should return your server IP# 1. Verify Docker container is running
docker ps | grep ctx0-instance
# 2. Check container logs for errors
docker logs ctx0-instance --tail 50
# 3. Test local container access
curl -I http://127.0.0.1:50080/
# Expected: HTTP/1.1 302 FOUND, Location: /login
# 4. Test Apache config
httpd -t # or apache2ctl -t
# 5. Check Apache is proxying correctly
curl -I http://127.0.0.1:80 -H "Host: a0.example.com"
curl -Ik https://127.0.0.1:443 -H "Host: a0.example.com"
# 6. Test external HTTPS access
curl -I https://ctx0.example.com/
# Expected: HTTP/2 302 with Location: /login
# 7. Test login page loads
curl -s https://ctx0.example.com/login | grep -i "<title>"
# Expected: <title>Login - CtxAI</title># Install wscat if needed
npm install -g wscat
# Test WebSocket connection
wscat -c wss://ctx0.example.com/wsCause: Incorrect .env configuration
Fix:
# Verify .env inside container
docker exec ctx0-instance cat /ctx0/.env
# Ensure format is:
# AUTH_LOGIN=username (NOT AUTH_LOGIN=true)
# AUTH_PASSWORD=password
# Restart after fixing
docker restart ctx0-instanceCause: DirectAdmin vhost overriding custom proxy config
Fix:
# Check vhost order
httpd -S 2>&1 | grep your-domain
# Ensure custom config loads BEFORE directadmin-vhosts.conf
# Use specific IP binding (e.g., 192.168.1.1:443) not wildcards (*:443)
# Restart Apache
systemctl restart httpdCause: Container not running or wrong port
Fix:
# Check container status
docker ps -a | grep ctx0-instance
# If stopped, check logs
docker logs ctx0-instance
# Restart container
docker start ctx0-instance
# Verify port binding
netstat -tlnp | grep 50080Cause: Container overloaded or unresponsive
Fix:
# Check container resource usage
docker stats ctx0-instance --no-stream
# Restart container
docker restart ctx0-instance
# Check for memory issues
free -hCause: Missing WebSocket proxy rules
Fix: Ensure these lines are in your vhost config:
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) ws://127.0.0.1:50080/$1 [P,L]Cause: Port conflict or Docker issue
Fix:
# Check what's using the port
netstat -tlnp | grep 50080
# Remove conflicting container
docker rm -f conflicting-container
# Check Docker daemon
systemctl status docker
journalctl -u docker --since "1 hour ago"Cause: Container needs restart to reload env
Fix:
docker restart ctx0-instance
# Verify env is loaded
docker exec ctx0-instance cat /ctx0/.env# Pull latest image
docker pull ctxos/ctxai:latest
# Stop and remove old container (data persists in volumes)
docker stop ctx0-instance
docker rm ctx0-instance
# Recreate with same settings
docker run -d --name ctx0-instance --restart unless-stopped -p 50080:80 -v /opt/ctx0-instance/.env:/ctx0/.env -v /opt/ctx0-instance/usr:/ctx0/usr -v /opt/ctxai:latest# Backup all instance data
tar -czvf ctx0-backup-$(date +%Y%m%d).tar.gz /opt/ctx0-instance/
# Key items to backup:
# - /opt/ctx0-instance/.env (configuration)
# - /opt/ctx0-instance/memory/ (agent memories)
# - /opt/ctx0-instance/work_dir/ (working files)# Check container health
docker ps --format "table {{.Names}} {{.Status}} {{.Ports}}"
# View recent logs
docker logs --tail 100 -f ctx0-instance
# Resource usage
docker stats ctx0-instance# Remove unused images
docker image prune -f
# Remove all unused Docker resources
docker system prune -f# Container Management
docker start ctx0-instance
docker stop ctx0-instance
docker restart ctx0-instance
docker logs ctx0-instance
docker exec -it ctx0-instance bash
# Apache Management
systemctl restart httpd # RHEL/AlmaLinux
systemctl restart apache2 # Debian/Ubuntu
httpd -t # Test config
# Quick Diagnostics
docker ps | grep a0
curl -I https://your-domain.com/login| Component | Path |
|---|---|
| Instance Data | /opt/ctx0-instance/ |
| Environment File | /opt/ctx0-instance/.env |
| Memory Storage | /opt/ctx0-instance/memory/ |
| Work Directory | /opt/ctx0-instance/work_dir/ |
| Logs | /opt/ctx0-instance/logs/ |
| Apache Config (Standard) | /etc/apache2/sites-available/ |
| Apache Config (DirectAdmin) | /etc/httpd/conf/extra/httpd-includes.conf |
| DirectAdmin SSL Certs | /usr/local/directadmin/data/users/USER/domains/ |
| Port | Purpose |
|---|---|
| 50080 | First CTX0 instance |
| 50081 | Second CTX0 instance |
| 50082 | Third CTX0 instance |
| 80 | HTTP (redirect to HTTPS) |
| 443 | HTTPS (main access) |
# CtxAI Configuration Template
# Copy and customize for each instance
# Authentication (REQUIRED for production)
AUTH_LOGIN=your_username
AUTH_PASSWORD=your_secure_password
# Optional: Additional settings
# Refer to CtxAI documentation for all optionsFor running multiple CTX0 instances on the same server:
# Instance 1: ctx0-primary on port 50080
mkdir -p /opt/ctx0-primary
# ... create .env, run container on port 50080
# Instance 2: ctx0-dev on port 50081
mkdir -p /opt/ctx0-dev
# ... create .env, run container on port 50081
# Instance 3: ctx0-backup on port 50082
mkdir -p /opt/ctx0-backup
# ... create .env, run container on port 50082Each instance needs:
- Unique container name
- Unique host port
- Separate data directory
- Separate domain/subdomain
- Separate Apache vhost config
This guide comes from successful CtxAI deployments across DirectAdmin and standard Linux environments.
Contributed by @hurtdidit in the CTX0 Community.