Skip to main content

Overview

Platzi Viewer can be deployed as a Docker container, providing an isolated and reproducible environment. The application runs on Python 3.13-slim with only the essential runtime dependencies.

Prerequisites

  • Docker 20.10 or higher
  • Docker Compose v2.0 or higher
  • Google Service Account credentials (JSON file)

Quick Start

1

Clone the repository

git clone <repository-url>
cd platzi-viewer
2

Set up credentials directory

Create a directory for your Google Service Account credentials:
mkdir -p secrets
cp /path/to/your/service_account.json secrets/
3

Create runtime data directory

mkdir -p runtime-data
This directory will store progress data and cached course information.
4

Start the container

docker-compose up -d
5

Access the application

Open your browser and navigate to:
http://localhost:8080

Docker Compose Configuration

The docker-compose.yml file defines the service configuration:
services:
  platzi-viewer:
    build:
      context: .
      dockerfile: Dockerfile
    image: platzi-viewer:latest
    container_name: platzi-viewer
    ports:
      - "8080:8080"
    environment:
      HOST: "0.0.0.0"
      PORT: "8080"
      PUBLIC_HOST: "localhost"
      PLATZI_DATA_PATH: "/data"
      GOOGLE_SERVICE_ACCOUNT_FILE: "/secrets/service_account.json"
    volumes:
      - ./runtime-data:/data
      - ./secrets:/secrets:ro
    restart: unless-stopped

Environment Variables

Required Variables

GOOGLE_SERVICE_ACCOUNT_FILE
string
required
Path to the Google Service Account JSON file inside the container.Default: /secrets/service_account.json

Network Configuration

HOST
string
default:"0.0.0.0"
The host address to bind the server to. Use 0.0.0.0 to accept connections from any network interface.
PORT
string
default:"8080"
The port number the server listens on.
PUBLIC_HOST
string
default:"localhost"
The hostname displayed in server logs and used for URL generation.

Storage Configuration

PLATZI_DATA_PATH
string
default:"/data"
Directory inside the container where application data is stored (progress, cache).
PLATZI_VIEWER_PATH
string
default:"/app"
Root directory of the application code inside the container.
MAX_PROGRESS_BYTES
integer
default:"2097152"
Maximum size (in bytes) allowed for the progress.json file (2MB default).

Python Configuration

PYTHONDONTWRITEBYTECODE
string
default:"1"
Prevents Python from writing .pyc files.
PYTHONUNBUFFERED
string
default:"1"
Forces Python output to be unbuffered for real-time logging.

Authentication Methods

Mount the credentials file as a read-only volume:
volumes:
  - ./secrets:/secrets:ro
environment:
  GOOGLE_SERVICE_ACCOUNT_FILE: "/secrets/service_account.json"

Option B: Environment Variable

Pass the entire JSON as an environment variable using an .env file:
  1. Create a .env file:
    GOOGLE_SERVICE_ACCOUNT_JSON='{"type":"service_account","project_id":"...","private_key":"..."}'
    
  2. Update docker-compose.yml:
    environment:
      GOOGLE_SERVICE_ACCOUNT_JSON: "${GOOGLE_SERVICE_ACCOUNT_JSON}"
    

Volume Mounts

Application Data Volume

volumes:
  - ./runtime-data:/data
Stores:
  • progress.json - User progress tracking
  • courses_cache.json - Cached course structure from Drive

Secrets Volume

volumes:
  - ./secrets:/secrets:ro
The :ro flag mounts the directory as read-only for security.

Dockerfile Details

The Dockerfile uses a multi-stage approach optimized for production:
FROM python:3.13-slim

# Environment configuration
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    HOST=0.0.0.0 \
    PORT=8080 \
    PUBLIC_HOST=localhost \
    PLATZI_VIEWER_PATH=/app \
    PLATZI_DATA_PATH=/data

# Install runtime dependencies
RUN pip install --no-cache-dir \
    requests==2.31.0 \
    google-api-python-client==2.108.0 \
    google-auth==2.23.0 \
    google-auth-oauthlib==1.1.0 \
    google-auth-httplib2==0.1.1

# Copy application code
COPY . /app
WORKDIR /app

# Create data directory
RUN mkdir -p /data

EXPOSE 8080

# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
  CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/api/health', timeout=3)"

CMD ["python", "-u", "server.py"]

Building from Source

Build the Docker Image

docker build -t platzi-viewer:latest .

Run without Docker Compose

docker run -d \
  --name platzi-viewer \
  -p 8080:8080 \
  -v $(pwd)/runtime-data:/data \
  -v $(pwd)/secrets:/secrets:ro \
  -e GOOGLE_SERVICE_ACCOUNT_FILE=/secrets/service_account.json \
  platzi-viewer:latest

Container Management

View Logs

docker-compose logs -f platzi-viewer

Stop the Container

docker-compose down

Restart the Container

docker-compose restart platzi-viewer

Update and Rebuild

docker-compose down
docker-compose build --no-cache
docker-compose up -d

Health Checks

The container includes a built-in health check that monitors the /api/health endpoint:
  • Interval: Every 30 seconds
  • Timeout: 5 seconds
  • Start Period: 15 seconds (grace period)
  • Retries: 3 attempts before marking unhealthy
Check container health:
docker ps
docker inspect platzi-viewer | grep -A 10 Health

Networking

Expose on Different Port

Change the host port mapping in docker-compose.yml:
ports:
  - "3000:8080"  # Access on port 3000

Access from Other Machines

Set PUBLIC_HOST to your server’s IP or domain:
environment:
  PUBLIC_HOST: "192.168.1.100"

Production Considerations

Always use read-only mounts (:ro) for sensitive files like service account credentials.

Security Best Practices

  1. Never commit service_account.json to version control
  2. Use .dockerignore to exclude sensitive files
  3. Run containers with limited privileges
  4. Use Docker secrets for production deployments
  5. Keep the base image updated: docker-compose pull

Performance Tuning

  • The container uses ThreadingHTTPServer for concurrent request handling
  • Mount /data volume on fast storage (SSD) for better cache performance
  • Consider using a reverse proxy (nginx/Caddy) for SSL termination

Monitoring

View real-time resource usage:
docker stats platzi-viewer

Troubleshooting

Container Won’t Start

Check logs for errors:
docker-compose logs platzi-viewer

Permission Denied Errors

Ensure the runtime-data directory is writable:
chmod 777 runtime-data

Can’t Connect to Drive API

Verify credentials are mounted correctly:
docker exec platzi-viewer ls -l /secrets/

Port Already in Use

Change the host port or stop conflicting services:
lsof -i :8080

Next Steps

Desktop App

Run as a native desktop application

Portable Executable

Build standalone Windows executables

Build docs developers (and LLMs) love