Files
Microsoft-Rewards-Bot/docs/docker-deployment.md

483 lines
13 KiB
Markdown

# Docker Deployment Guide
Complete guide for containerized deployment with built-in scheduling.
## 📂 Docker File Structure
```
Microsoft-Rewards-Bot/
├── docker/ # All Docker-related files
│ ├── Dockerfile # Multi-stage build configuration
│ ├── compose.yaml # Service definition
│ ├── entrypoint.sh # Container initialization
│ ├── run_daily.sh # Daily execution wrapper + locking
│ └── crontab.template # Cron job template
├── src/
│ ├── accounts.jsonc # Your accounts (volume mount)
│ └── config.jsonc # Bot configuration (volume mount)
└── package.json # Build scripts
```
**Important:** All Docker files are in the `docker/` folder, but the build context is the **project root** (to access `src/`, `package.json`, etc.)
---
## 🚀 Quick Start
### 1. Build the Image
**Option A: Using npm script (recommended)**
```bash
# From project root
npm run docker:build
```
**Option B: Direct Docker command**
```bash
# From project root
docker build -f docker/Dockerfile -t microsoft-rewards-bot .
```
**Why from root?** The Dockerfile needs access to `src/`, `package.json`, `tsconfig.json`. Docker can't copy files outside the build context.
### 2. Configure Environment
Edit `docker/compose.yaml`:
```yaml
environment:
TZ: "America/New_York" # Your timezone
CRON_SCHEDULE: "0 9 * * *" # Daily at 9 AM
RUN_ON_START: "true" # Run immediately on start
```
### 3. Start the Container
**Option A: Using npm script**
```bash
npm run docker:compose
```
**Option B: Direct docker compose command**
```bash
docker compose -f docker/compose.yaml up -d
```
**Note:** Volumes in `compose.yaml` use relative paths from `docker/`:
- `./src/``docker/../src/` (goes to project root)
- `./sessions/``docker/sessions/` (local to docker folder)
## 📋 Configuration Files
The container needs access to your configuration files via volume mounts:
```yaml
volumes:
# Read-only mounts for configuration (prevents accidental container edits)
- ../src/accounts.jsonc:/usr/src/microsoft-rewards-bot/accounts.jsonc:ro
- ../src/config.jsonc:/usr/src/microsoft-rewards-bot/config.jsonc:ro
# Read-write mount for persistent login sessions
- ../sessions:/usr/src/microsoft-rewards-bot/sessions
```
**Paths explained:**
- `../src/accounts.jsonc` = `docker/../src/accounts.jsonc` (relative from compose.yaml location, goes to project root)
- `../sessions` = `docker/../sessions/` (project root sessions folder)
**Before starting:**
1. Create `src/accounts.jsonc` (copy from `src/accounts.example.jsonc`)
2. Edit `src/config.jsonc` with your settings
3. (Optional) Create `sessions/` directory at project root for persistent login
---
## 🏗️ Architecture & Build Process
### Two-Stage Docker Build
**Stage 1: Builder**
```dockerfile
FROM node:22-slim AS builder
# 1. Install dependencies + dev dependencies
# 2. Compile TypeScript → JavaScript (dist/)
# 3. Install Playwright Chromium
# 4. Remove dev dependencies (keep only production)
```
**Stage 2: Runtime**
```dockerfile
FROM node:22-slim AS runtime
# 1. Install minimal system libraries for Chromium
# 2. Copy compiled code + dependencies from builder
# 3. Copy runtime scripts (entrypoint.sh, run_daily.sh)
# 4. Configure cron daemon
```
**Why two stages?**
- Smaller final image (~800 MB vs 1.5 GB)
- No build tools in production
- Security: no dev dependencies
### Build Context Explained
```bash
# ❌ WRONG: Building from docker/ folder
cd docker
docker build -t bot .
# Error: Cannot find package.json, src/, etc.
# ✅ CORRECT: Build from root, specify Dockerfile location
cd /path/to/Microsoft-Rewards-Bot
docker build -f docker/Dockerfile -t bot .
# Success: Access to all project files
```
**The Dockerfile copies files relative to project root:**
```dockerfile
COPY package.json tsconfig.json ./ # From project root
COPY src/ ./src/ # From project root
COPY docker/run_daily.sh ./docker/ # Subfolder
```
---
## 🔧 Environment Variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `CRON_SCHEDULE` | ✅ Yes | N/A | Cron expression (e.g., `0 9 * * *`) |
| `TZ` | ❌ No | `UTC` | Timezone (e.g., `America/New_York`) |
| `RUN_ON_START` | ❌ No | `false` | Run immediately on container start |
| `SKIP_RANDOM_SLEEP` | ❌ No | `false` | Skip random delay before execution |
| `MIN_SLEEP_MINUTES` | ❌ No | `5` | Minimum random delay (minutes) |
| `MAX_SLEEP_MINUTES` | ❌ No | `50` | Maximum random delay (minutes) |
| `STUCK_PROCESS_TIMEOUT_HOURS` | ❌ No | `8` | Kill stuck processes after N hours |
| `PLAYWRIGHT_BROWSERS_PATH` | 🔒 Fixed | `0` | Use browsers in node_modules (DO NOT CHANGE) |
| `FORCE_HEADLESS` | 🔒 Fixed | `1` | Headless mode (DO NOT CHANGE) |
| `NODE_ENV` | 🔒 Fixed | `production` | Production environment (DO NOT CHANGE) |
## 📅 Scheduling Examples
Use [crontab.guru](https://crontab.guru) to create cron expressions.
| Schedule | Cron Expression | Description |
|----------|----------------|-------------|
| Daily at 9 AM | `0 9 * * *` | Once per day |
| Every 6 hours | `0 */6 * * *` | 4 times daily |
| Twice daily (9 AM, 9 PM) | `0 9,21 * * *` | Morning & evening |
| Weekdays at 8 AM | `0 8 * * 1-5` | Monday-Friday only |
| Random time (7-8 AM) | `0 7 * * *` + random sleep | Use `MIN_SLEEP_MINUTES`/`MAX_SLEEP_MINUTES` |
**Example with Random Delay:**
```yaml
environment:
CRON_SCHEDULE: "0 7 * * *" # Start scheduling at 7 AM
MIN_SLEEP_MINUTES: "0" # No minimum delay
MAX_SLEEP_MINUTES: "60" # Up to 1 hour delay
# Result: Runs between 7:00 AM - 8:00 AM randomly
```
## 🔍 Monitoring
### View Logs
```bash
docker logs -f microsoft-rewards-bot
```
### Check Container Status
```bash
docker ps -a | grep microsoft-rewards-bot
```
### Health Check
```bash
docker inspect microsoft-rewards-bot --format='{{.State.Health.Status}}'
```
Expected output: `healthy`
### Execute Commands Inside Container
```bash
# Check cron status
docker exec microsoft-rewards-bot crontab -l
# Check timezone
docker exec microsoft-rewards-bot date
# List running processes
docker exec microsoft-rewards-bot ps aux
```
## 🛠️ Troubleshooting
### ❌ Build Error: "Cannot find module 'package.json'"
**Cause:** Wrong build context - building from `docker/` instead of project root.
**Solution:**
```bash
# ❌ WRONG
cd docker
docker build -t bot .
# ✅ CORRECT (from project root)
docker build -f docker/Dockerfile -t bot .
# Or use npm script
npm run docker:build
```
### ❌ Build Error: "COPY failed: file not found: docker/run_daily.sh"
**Cause:** `.dockerignore` file excludes `docker/` folder.
**Solution:** Check `.dockerignore` - it should NOT contain `docker/`:
```ignore
# ❌ BAD
docker/
# ✅ GOOD (current config)
scripts/installer/
```
### ❌ "Chromium not installed" After First Run
**Symptoms:**
- Bot works on first run
- Fails on subsequent scheduled runs with "Chromium not installed"
**Root Cause:**
Fixed in v2.56.7+! Chromium browser files are now copied correctly from builder stage.
**Solution:**
```bash
# Rebuild with latest Dockerfile (includes fix)
npm run docker:build
docker compose -f docker/compose.yaml down
docker compose -f docker/compose.yaml up -d
```
**Verification:**
Check that Chromium is present in the container:
```bash
docker exec microsoft-rewards-bot ls -la /usr/src/microsoft-rewards-bot/node_modules/@playwright/
```
You should see `browser-chromium/` directory.
### ❌ Container Exits Immediately
**Check logs:**
```bash
docker logs microsoft-rewards-bot
```
**Common causes:**
1. **Missing CRON_SCHEDULE:** Set environment variable in compose.yaml
```yaml
environment:
CRON_SCHEDULE: "0 9 * * *" # Required!
```
2. **Invalid cron expression:** Validate at https://crontab.guru
3. **Script not executable:** Dockerfile should handle this automatically
```dockerfile
COPY --chmod=755 docker/entrypoint.sh /usr/local/bin/entrypoint.sh
```
**Fix:**
```bash
# Update compose.yaml with correct CRON_SCHEDULE
docker compose -f docker/compose.yaml down
docker compose -f docker/compose.yaml up -d
```
### ❌ "Permission denied: entrypoint.sh"
**Cause:** Script not executable or Windows CRLF line endings.
**Solution:** The Dockerfile handles both automatically:
```dockerfile
COPY --chmod=755 docker/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN sed -i 's/\r$//' /usr/local/bin/entrypoint.sh # Convert CRLF → LF
```
If still failing, manually fix line endings:
```bash
# Linux/Mac
dos2unix docker/entrypoint.sh docker/run_daily.sh
# Or with sed
sed -i 's/\r$//' docker/entrypoint.sh
sed -i 's/\r$//' docker/run_daily.sh
```
### ❌ Volumes Not Mounting
### ❌ Volumes Not Mounting
**Check if files exist:**
```bash
# From docker/ folder
ls -la ../src/accounts.jsonc ../src/config.jsonc
# Or from project root
ls -la src/accounts.jsonc src/config.jsonc
```
**Verify volume paths in running container:**
```bash
docker inspect microsoft-rewards-bot | grep -A 10 Mounts
```
**Fix paths in compose.yaml:**
```yaml
# Paths are relative to compose.yaml location (docker/)
volumes:
- ../src/accounts.jsonc:/usr/src/microsoft-rewards-bot/accounts.jsonc:ro
# This resolves to: docker/../src/accounts.jsonc (project root)
# Note: Files mount to project root, NOT dist/ (Load.ts searches multiple locations)
```
---
## 📚 Complete File Reference
### `docker/Dockerfile`
Multi-stage build configuration:
- **Builder stage:** Compiles TypeScript, installs Playwright
- **Runtime stage:** Minimal image with only production dependencies
### `docker/compose.yaml`
Service definition with:
- Build context (points to project root)
- Volume mounts (configuration files)
- Environment variables (scheduling, timezone)
- Resource limits (CPU, memory)
- Health checks (cron daemon monitoring)
### `docker/entrypoint.sh`
Container initialization script:
1. Configures timezone from `TZ` env var
2. Validates `CRON_SCHEDULE` is set
3. Optionally runs bot immediately (`RUN_ON_START=true`)
4. Templates cron job with environment variables
5. Starts cron daemon in foreground
### `docker/run_daily.sh`
Daily execution wrapper:
- File-based locking (prevents concurrent runs)
- Self-healing lockfile validation
- Random sleep delay (anti-detection)
- Stuck process killer (timeout after N hours)
- Executes `npm start`
### `docker/crontab.template`
Cron job template with variable interpolation:
```bash
${CRON_SCHEDULE} TZ=${TZ} /bin/bash /usr/src/microsoft-rewards-bot/docker/run_daily.sh >> /proc/1/fd/1 2>&1
```
Expanded by `entrypoint.sh` using `envsubst`.
---
## 🎯 Build & Deployment Checklist
### Pre-Build Checklist
- [ ] Files exist: `src/accounts.jsonc`, `src/config.jsonc`
- [ ] Docker installed and running
- [ ] At project root (not in `docker/` folder)
- [ ] `.dockerignore` does NOT exclude `docker/` folder
### Build Steps
```bash
# 1. Clean previous builds (optional)
docker rmi microsoft-rewards-bot
# 2. Build image
npm run docker:build
# Or: docker build -f docker/Dockerfile -t microsoft-rewards-bot .
# 3. Verify image created
docker images | grep microsoft-rewards-bot
```
### Deployment Steps
```bash
# 1. Configure compose.yaml
# - Set CRON_SCHEDULE (required)
# - Set TZ (optional, default UTC)
# - Set RUN_ON_START (optional, default false)
# 2. Start container
npm run docker:compose
# Or: docker compose -f docker/compose.yaml up -d
# 3. Verify running
docker ps | grep microsoft-rewards-bot
# 4. Check logs
docker logs -f microsoft-rewards-bot
# 5. Verify health
docker inspect microsoft-rewards-bot --format='{{.State.Health.Status}}'
```
---
## 🔍 Debugging Commands
### Check Build Context
```bash
# List files Docker can see during build
docker build -f docker/Dockerfile -t test . --progress=plain 2>&1 | grep "COPY"
```
### Inspect Running Container
```bash
# Shell access
docker exec -it microsoft-rewards-bot bash
# Check environment variables
docker exec microsoft-rewards-bot env
# Check cron jobs
docker exec microsoft-rewards-bot crontab -l
# Check timezone
docker exec microsoft-rewards-bot date
# Check Playwright browsers
docker exec microsoft-rewards-bot ls -la node_modules/@playwright/
# Check running processes
docker exec microsoft-rewards-bot ps aux
```
### Test Scripts Manually
```bash
# Test entrypoint
docker exec microsoft-rewards-bot /usr/local/bin/entrypoint.sh
# Test run_daily (bypass lock)
docker exec microsoft-rewards-bot bash -c "rm -f /tmp/run_daily.lock && docker/run_daily.sh"
# Test bot directly
docker exec microsoft-rewards-bot npm start
```
---
## 📖 Additional Resources
- **Build System:** [docs/build-system.md](build-system.md)
- **Scheduling:** [src/scheduler/README.md](../src/scheduler/README.md)
- **Configuration:** [src/config.jsonc](../src/config.jsonc) (inline comments)
- **Accounts:** [docs/accounts.md](accounts.md)
---
**Last Updated:** November 2025
**Applies To:** v2.56.7+