9.1 KiB
Docker Deployment Guide
Prerequisites
- Docker or Podman installed
- Gitea instance with Container Registry enabled
GITEA_TOKENsecret configured in repository settings
Building
Local Build
# Build image
docker build -t aitrade:local .
# Build with specific tag
docker build -t aitrade:v1.0.0 .
Automated Build (Gitea Actions)
The CI/CD pipeline automatically builds and pushes images on:
- Push to main/master/develop: Creates
latestand branch-specific tags - Git tags (v)*: Creates semantic version tags (
v1.0.0,1.0,1) - Pull requests: Build-only, no push
Image Tags:
gitea.yourdomain.com/username/aitrade:latest
gitea.yourdomain.com/username/aitrade:main
gitea.yourdomain.com/username/aitrade:main-abc123def
gitea.yourdomain.com/username/aitrade:v1.0.0
gitea.yourdomain.com/username/aitrade:1.0
gitea.yourdomain.com/username/aitrade:1
Running
Docker Compose (Recommended)
# Start application
docker-compose up -d
# View logs
docker-compose logs -f aitrade
# Stop application
docker-compose down
# Restart application
docker-compose restart aitrade
With Ollama (LLM Sentiment)
# Start both services
docker-compose --profile llm up -d
# Pull Mistral model (first time only)
docker exec ollama ollama pull mistral
# Verify Ollama is running
curl http://localhost:11434/api/version
# Enable LLM in aitrade (edit docker-compose.yaml)
# Set: LLM_SCORER_ENABLED: "true"
# Restart aitrade
docker-compose restart aitrade
Standalone Container
# Pull from registry
docker pull gitea.yourdomain.com/username/aitrade:latest
# Run with default settings
docker run -d \
--name aitrade \
-p 8080:8080 \
-v $(pwd)/data:/app/data \
gitea.yourdomain.com/username/aitrade:latest
# Run with custom configuration
docker run -d \
--name aitrade \
-p 8080:8080 \
-v $(pwd)/data:/app/data \
-v $(pwd)/config.yaml:/app/config.yaml:ro \
-e CONFIG_FILE=/app/config.yaml \
gitea.yourdomain.com/username/aitrade:latest
# Run with environment variables
docker run -d \
--name aitrade \
-p 8080:8080 \
-v $(pwd)/data:/app/data \
-e TRADING_STRATEGY=normal \
-e DRY_RUN=true \
-e TRADING_ENABLED=false \
-e LLM_SCORER_ENABLED=false \
gitea.yourdomain.com/username/aitrade:latest
Configuration
Option 1: Environment Variables
Pass environment variables via -e flag or Docker Compose environment: section.
docker run -d \
-e TRADING_STRATEGY=aggressive \
-e MAX_TRADES_PER_HOUR=12 \
-e DRY_RUN=true \
...
Option 2: Config File (YAML)
Mount a config.yaml file into the container:
docker run -d \
-v $(pwd)/config.yaml:/app/config.yaml:ro \
-e CONFIG_FILE=/app/config.yaml \
...
Option 3: Docker Compose
Edit docker-compose.yaml and modify the environment: section.
Networking
IB Gateway on Host
If IB Gateway runs on the host machine, use host.docker.internal:
environment:
IB_GATEWAY_HOST: host.docker.internal
IB_GATEWAY_PORT: "4001"
Ollama on Host
environment:
LLM_SCORER_ENABLED: "true"
LLM_SCORER_ENDPOINT: http://host.docker.internal:11434
Custom Network
# Create network
docker network create trading-net
# Run IB Gateway container
docker run -d --name ib-gateway --network trading-net your-ib-image
# Run aitrade
docker run -d \
--name aitrade \
--network trading-net \
-e IB_GATEWAY_HOST=ib-gateway \
-e IB_GATEWAY_PORT=4001 \
...
Persistence
Database
Mount /app/data to persist SQLite database:
docker run -d \
-v $(pwd)/data:/app/data \
...
Important: Ensure the directory is writable by UID 1000 (trader user).
mkdir -p data
chown -R 1000:1000 data
Config File
Mount config as read-only:
docker run -d \
-v $(pwd)/config.yaml:/app/config.yaml:ro \
-e CONFIG_FILE=/app/config.yaml \
...
Health Checks
The container includes a built-in health check at /health:
Docker Health Status
# Check health status
docker inspect --format='{{.State.Health.Status}}' aitrade
# Output: healthy, unhealthy, or starting
# View health check logs
docker inspect --format='{{range .State.Health.Log}}{{.Output}}{{end}}' aitrade
# Manual check
curl http://localhost:8080/health
# Response: {"status":"healthy"}
Custom Healthcheck Binary
The image includes a lightweight healthcheck binary (/app/healthcheck) for internal health checks:
# Run healthcheck from inside container
docker exec aitrade /app/healthcheck localhost 8080
# Custom host/port
docker exec aitrade /app/healthcheck 127.0.0.1 8080
# Exit code 0 = healthy, 1 = unhealthy
This allows health checks in Distroless without needing curl/wget.
Monitoring
Logs
# View logs
docker logs aitrade
# Follow logs
docker logs -f aitrade
# Last 100 lines
docker logs --tail 100 aitrade
# Docker Compose
docker-compose logs -f aitrade
Metrics
Access the web dashboard at http://localhost:8080:
- Account balance
- Active trades
- Trade history
- News sentiment
- Whitelist management
Updating
Pull Latest Image
# Stop container
docker stop aitrade
docker rm aitrade
# Pull latest
docker pull gitea.yourdomain.com/username/aitrade:latest
# Start with same settings
docker run -d \
--name aitrade \
-p 8080:8080 \
-v $(pwd)/data:/app/data \
gitea.yourdomain.com/username/aitrade:latest
Docker Compose
# Pull latest
docker-compose pull aitrade
# Restart
docker-compose up -d aitrade
Zero-Downtime Update
# Pull new image
docker pull gitea.yourdomain.com/username/aitrade:latest
# Start new container with different name
docker run -d \
--name aitrade-new \
-p 8081:8080 \
-v $(pwd)/data:/app/data \
gitea.yourdomain.com/username/aitrade:latest
# Verify new container is healthy
curl http://localhost:8081/health
# Switch port mapping (update reverse proxy or load balancer)
# Then stop old container
docker stop aitrade
docker rm aitrade
# Rename new container
docker rename aitrade-new aitrade
Troubleshooting
Container Won't Start
# Check logs
docker logs aitrade
# Check health status
docker inspect --format='{{.State.Health.Status}}' aitrade
# Verify permissions
ls -la data/
# Should be owned by UID 1000
Database Errors
# Check database file
ls -la data/aitrade.db
# Reset database (deletes all data!)
docker stop aitrade
rm -f data/aitrade.db*
docker start aitrade
IB Gateway Connection
# Check IB Gateway is running
netstat -an | grep 4001
# Test from container
docker exec aitrade sh -c "nc -zv host.docker.internal 4001"
Ollama Connection
# Test Ollama from host
curl http://localhost:11434/api/version
# Test from container
docker exec aitrade sh -c "wget -qO- http://host.docker.internal:11434/api/version"
Security
Non-Root User
The container runs as user trader (UID 1000) by default.
Network Isolation
Run on a dedicated network:
docker network create --internal trading-net
Secrets
Never commit secrets to the repository. Use:
- Docker secrets
- Environment files (
.env) - Kubernetes secrets
- Vault
# Using .env file
docker run -d \
--env-file .env \
...
Production Deployment
Systemd Service
[Unit]
Description=AI Trading Application
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/aitrade
ExecStartPre=-/usr/bin/docker stop aitrade
ExecStartPre=-/usr/bin/docker rm aitrade
ExecStart=/usr/bin/docker run -d \
--name aitrade \
--restart unless-stopped \
-p 8080:8080 \
-v /opt/aitrade/data:/app/data \
-v /opt/aitrade/config.yaml:/app/config.yaml:ro \
-e CONFIG_FILE=/app/config.yaml \
gitea.yourdomain.com/username/aitrade:latest
ExecStop=/usr/bin/docker stop aitrade
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl enable aitrade
sudo systemctl start aitrade
sudo systemctl status aitrade
Kubernetes
See k8s/ directory for Kubernetes manifests (deployment, service, configmap, secrets).
Backup
Database Backup
Since the application uses SQLite in default mode (single file), backups are straightforward:
# Simple copy (application should be stopped)
docker stop aitrade
cp data/aitrade.db backups/aitrade-$(date +%Y%m%d).db
docker start aitrade
# Or use SQLite backup command (can run while app is running)
docker exec aitrade sqlite3 /app/data/aitrade.db ".backup '/app/data/backup-$(date +%Y%m%d).db'"
# Copy to host
docker cp aitrade:/app/data/backup-20260628.db ./backups/
# Automated backup (cron) - runs while app is running
0 2 * * * docker exec aitrade sqlite3 /app/data/aitrade.db ".backup '/app/data/backup-$(date +\%Y\%m\%d).db'"
Note: SQLite .backup command is safe to run while the application is running. Simple file copy should only be done when the application is stopped.
Full Backup
# Backup entire data directory (stop app first)
docker stop aitrade
tar -czf aitrade-backup-$(date +%Y%m%d).tar.gz data/
docker start aitrade