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

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:

  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

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:

  1. Missing CRON_SCHEDULE: Set environment variable in compose.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

    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:

  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:

${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

# 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


Last Updated: November 2025
Applies To: v2.56.7+