Overview
Self-hosting business intelligence tools gives your organization full control over data, privacy, and costs. In this guide, I walk through deploying a complete BI stack on a VPS using Docker Compose, secured with a reverse proxy and custom subdomains.
What You'll Need
- A VPS with at least 4GB RAM and 2 vCPUs (Ubuntu 22.04 LTS recommended)
- A domain name with DNS access
- Basic Linux command line knowledge
- Understanding of Docker concepts (images, containers, volumes)
Architecture Overview
Deployment Architecture
Internet
Reverse Proxy + SSL
Docker Network
BI App + DB
Step 1: VPS Initial Setup
Start with a fresh Ubuntu 22.04 LTS server. Secure it first:
# Update system
sudo apt update && sudo apt upgrade -y
# Create non-root user
sudo adduser deploy
sudo usermod -aG sudo deploy
# Configure SSH key authentication
ssh-copy-id deploy@your-server-ip
# Disable password authentication
sudo nano /etc/ssh/sshd_config
# Set: PasswordAuthentication no
sudo systemctl restart sshd
# Enable firewall
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
Step 2: Install Docker & Docker Compose
# Install Docker
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker deploy
# Install Docker Compose plugin
sudo apt install docker-compose-plugin -y
# Verify installation
docker --version
docker compose version
Step 3: Docker Compose Configuration
Create a project directory and define your services:
# docker-compose.yml
version: "3.8"
services:
bi-app:
image: your-bi-platform:latest
container_name: bi-app
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
environment:
- DB_HOST=db
- DB_PORT=5432
- DB_NAME=bi_data
- DB_USER=${DB_USER}
- DB_PASS=${DB_PASS}
volumes:
- bi-data:/app/data
depends_on:
- db
networks:
- app-network
db:
image: postgres:15-alpine
container_name: bi-db
restart: unless-stopped
environment:
- POSTGRES_DB=bi_data
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASS}
volumes:
- db-data:/var/lib/postgresql/data
networks:
- app-network
volumes:
bi-data:
db-data:
networks:
app-network:
driver: bridge
Security Note
Never hardcode credentials in docker-compose.yml. Use a .env file and add it to .gitignore. For production, use Docker secrets or a vault solution.
Step 4: Reverse Proxy with SSL
Use a reverse proxy to handle SSL termination and route traffic to containers:
- Point your subdomain DNS record (e.g.,
bi.yourdomain.com) to your VPS IP - Configure the reverse proxy to forward traffic to the container's internal port
- Enable automatic SSL certificate provisioning (Let's Encrypt)
- Set up automatic certificate renewal
Step 5: Backup Strategy
# backup.sh - Run via cron daily
#!/bin/bash
BACKUP_DIR="/opt/backups"
DATE=$(date +%Y%m%d)
# Backup database
docker exec bi-db pg_dump -U $DB_USER bi_data | \
gzip > "$BACKUP_DIR/db-$DATE.sql.gz"
# Backup volumes
docker run --rm \
-v bi-data:/source \
-v $BACKUP_DIR:/backup \
alpine tar czf "/backup/bi-data-$DATE.tar.gz" -C /source .
# Retain last 14 days
find $BACKUP_DIR -mtime +14 -delete
echo "Backup completed: $DATE"
Key Takeaways
Deployment Checklist
- Secure the VPS before deploying anything (SSH keys, firewall, updates)
- Use Docker Compose for reproducible deployments
- Never expose container ports directly — always use a reverse proxy
- Implement automated backups from day one
- Monitor container health and resource usage
- Use environment variables for all credentials
- Set up log rotation to prevent disk space issues