13 KiB
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)
# From project root
npm run docker:build
Option B: Direct Docker command
# 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:
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
npm run docker:compose
Option B: Direct docker compose command
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:
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:
- Create
src/accounts.jsonc(copy fromsrc/accounts.example.jsonc) - Edit
src/config.jsoncwith your settings - (Optional) Create
sessions/directory at project root for persistent login
🏗️ Architecture & Build Process
Two-Stage Docker Build
Stage 1: Builder
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
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
# ❌ 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:
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 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:
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
docker logs -f microsoft-rewards-bot
Check Container Status
docker ps -a | grep microsoft-rewards-bot
Health Check
docker inspect microsoft-rewards-bot --format='{{.State.Health.Status}}'
Expected output: healthy
Execute Commands Inside Container
# 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:
# ❌ 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/:
# ❌ 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:
# 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:
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:
docker logs microsoft-rewards-bot
Common causes:
-
Missing CRON_SCHEDULE: Set environment variable in compose.yaml
environment: CRON_SCHEDULE: "0 9 * * *" # Required! -
Invalid cron expression: Validate at https://crontab.guru
-
Script not executable: Dockerfile should handle this automatically
COPY --chmod=755 docker/entrypoint.sh /usr/local/bin/entrypoint.sh
Fix:
# 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:
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:
# 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:
# 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:
docker inspect microsoft-rewards-bot | grep -A 10 Mounts
Fix paths in compose.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:
- Configures timezone from
TZenv var - Validates
CRON_SCHEDULEis set - Optionally runs bot immediately (
RUN_ON_START=true) - Templates cron job with environment variables
- 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:
${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) .dockerignoredoes NOT excludedocker/folder
Build Steps
# 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
# 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
# List files Docker can see during build
docker build -f docker/Dockerfile -t test . --progress=plain 2>&1 | grep "COPY"
Inspect Running Container
# 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
# 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
- Scheduling: src/scheduler/README.md
- Configuration: src/config.jsonc (inline comments)
- Accounts: docs/accounts.md
Last Updated: November 2025
Applies To: v2.56.7+