Morning Drive

Deployment

This guide covers deploying Morning Drive to production environments.

Development vs Production: For local development with auto-reload, see the Development guide and use ./dev.sh. This page covers production deployment.

Docker Hub Deployment (Recommended)

The recommended approach is to build and push to Docker Hub, then pull the image on your production server. This method:

  • Keeps your production server clean (no build tools needed)
  • Enables easy rollbacks to previous versions
  • Auto-restarts on server reboot

Step 1: Build & Push (Development Machine)

On your development machine, build and push the Docker image:

cd backend

# Build and push with a version tag
export DOCKER_USERNAME=usaiinc
./scripts/build-and-push.sh v1.0.0

The script will:

  • Build the Docker image for linux/amd64
  • Tag it with your version and "latest"
  • Push to Docker Hub
Docker Hub Account: The project uses Docker Hub username usaiinc (created via GitHub authentication). Run docker login to authenticate before pushing.

Step 2: Deploy (Production Server)

Use the deploy script to copy files to your server:

cd backend

# Deploy to altair (default)
./scripts/deploy-to-server.sh

# Or specify a different SSH host
./scripts/deploy-to-server.sh myserver

The script copies docker-compose.prod.yml, .env.prod.example, and the assets/ folder to ~/morning-drive on the target server.

SSH Config: The default target is altair. Set up your SSH config in ~/.ssh/config for passwordless access.

After running the script, SSH to your server and configure the environment:

# SSH to your server
ssh altair
cd ~/morning-drive

# Edit .env with your API keys
nano .env

Required .env Configuration

# API Keys
ANTHROPIC_API_KEY=sk-ant-xxxxx
ELEVENLABS_API_KEY=xxxxx

# Admin password - CHANGE THIS!
ADMIN_PASSWORD=your-secure-password

Step 3: Start Services

# Pull and start (will auto-restart on boot)
docker compose -f docker-compose.prod.yml up -d

# View logs
docker compose -f docker-compose.prod.yml logs -f

# Check status
docker compose -f docker-compose.prod.yml ps
Auto-restart: The production compose file uses restart: always, so services automatically start when the server boots.

Updating to a New Version

# On development machine: build and push new version
./scripts/build-and-push.sh v1.1.0

# On production server: pull and restart
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d

Alternative: Build on Production Server

If you prefer to build directly on the production server instead of using Docker Hub:

cd backend
cp .env.example .env
nano .env  # Add your API keys
docker compose -f docker-compose.yml up -d --build

Note: For production, explicitly specify -f docker-compose.yml to skip the development override.


Services Overview

ServicePortDescription
morning-drive5000Main FastAPI application
minio9000, 9001S3-compatible storage for music
scheduler-Optional background job scheduler

Environment Variables

VariableRequiredDescription
ANTHROPIC_API_KEYYesClaude API key for content generation
ELEVENLABS_API_KEYYesElevenLabs API key for TTS
ADMIN_PASSWORDYesPassword for admin panel access
IMAGE_TAGNoDocker image tag (default: latest)
MINIO_ACCESS_KEYNoMinIO access key (default: minioadmin)
MINIO_SECRET_KEYNoMinIO secret key (default: minioadmin)
ELEVENLABS_CUSTOM_VOICE_IDSNoComma-separated custom voice IDs

Port Configuration

The production setup exposes the API on port 5000 (mapped to internal port 8000). This is configured for use with Cloudflare Tunnels.

Running the Scheduler

The scheduler service handles automatic briefing generation at scheduled times:

# Start with scheduler enabled
docker-compose --profile with-scheduler up -d

Production Considerations

Security: Always change the default admin password and MinIO credentials in production.

Reverse Proxy Setup (nginx)

server {
    listen 80;
    server_name your-domain.com;

    location / {
        proxy_pass http://localhost:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

SSL/TLS

Use Let's Encrypt with Certbot for free SSL certificates:

sudo certbot --nginx -d your-domain.com

Persistent Storage

The Docker setup uses named volumes for data persistence:

  • morning-drive-data - SQLite database and generated audio files
  • morning-drive-minio-data - Uploaded music files

Data persists across container restarts and updates. Deploying new compose files will NOT overwrite your data. Volumes are only deleted if you explicitly run docker compose down -v.

Syncing Music Library

To sync music pieces from your local development environment to production:

cd backend
./scripts/sync-music-to-server.sh

This script exports music files from local MinIO and database records, then imports them into production. It adds/updates files without deleting existing production data.

Health Checks

Monitor your deployment with the health endpoint:

curl http://localhost:8000/health
# {"status": "healthy", "service": "morning-drive"}

Or use the Admin Health Dashboard for a detailed view of all services.

Managing the Deployment

# View running services
docker compose -f docker-compose.prod.yml ps

# View logs
docker compose -f docker-compose.prod.yml logs -f

# Restart services
docker compose -f docker-compose.prod.yml restart

# Stop all services
docker compose -f docker-compose.prod.yml down

# Stop and remove volumes (WARNING: deletes all data)
docker compose -f docker-compose.prod.yml down -v

Troubleshooting

IssueSolution
Container won't startCheck logs: docker compose logs morning-drive
Can't pull imageVerify DOCKER_USERNAME in .env matches your Docker Hub account
MinIO connection failedEnsure MinIO container is healthy: docker compose ps
API keys not workingCheck .env file has no quotes around values