Skip to main content
Deploy Safe Settings using Docker for a quick, portable deployment that works locally or in any cloud environment.

Prerequisites

  • Docker installed (Docker 20.x or later)
  • Docker Compose (optional, but recommended)
  • GitHub App created with credentials
  • Node.js 18.x or later for local builds

Quick Start

1
Clone the Repository
2
git clone https://github.com/github/safe-settings.git
cd safe-settings/
3
Configure Environment
4
Create .env file with your GitHub App credentials:
5
cp .env.example .env
6
Edit .env with your values:
7
# Required
APP_ID=123456
WEBHOOK_SECRET=your-webhook-secret
PRIVATE_KEY="$(cat private-key.pem | base64)"

# Optional
ADMIN_REPO=admin
LOG_LEVEL=info
NODE_ENV=production
8
For Docker deployments, use PRIVATE_KEY (base64 encoded) instead of PRIVATE_KEY_PATH to avoid file path issues.
9
Build the Docker Image
10
docker build -t safe-settings .
11
The build process:
12
  • Uses Node.js 22 Alpine base image
  • Installs dependencies with npm ci
  • Copies application files
  • Exposes port 3000
  • 14
    docker-compose --env-file .env up -d
    
    15
    This starts the container in detached mode.

    Dockerfile Structure

    The Safe Settings Dockerfile is optimized for production:
    FROM node:22-alpine
    WORKDIR /opt/safe-settings
    ENV NODE_ENV production
    
    ## Set the Labels
    LABEL version="1.0" \
          description="Probot app which is a modified version of Settings Probot GitHub App" \
          maintainer="GitHub Professional Services <[email protected]>"
    
    ## These files are copied separately to allow updates
    ## to the image to be as small as possible
    COPY  package*.json /opt/safe-settings/
    COPY  index.js /opt/safe-settings/
    COPY  lib /opt/safe-settings/lib
    
    ## Install the app and dependencies
    RUN npm ci
    
    ## This app will listen on port 3000
    EXPOSE 3000
    
    USER node
    
    ## This does not start properly when using the ['npm','start'] format
    ## so stick with just calling it outright
    CMD npm start
    

    Running Docker Container

    Using Docker Compose

    The recommended approach using docker-compose:
    docker-compose --env-file .env up -d
    

    Using Docker CLI

    Detached Mode (Background)

    Run the container in the background:
    docker run -d \
      --name safe-settings \
      -p 3000:3000 \
      --env-file .env \
      safe-settings
    
    Verify it’s running:
    docker ps
    
    You should see safe-settings in the list.

    Interactive Mode (Foreground)

    For debugging, run in interactive mode:
    docker run -it \
      -p 3000:3000 \
      --env-file .env \
      safe-settings
    
    This shows logs directly in your terminal.

    With Individual Environment Variables

    Instead of using --env-file:
    docker run -d \
      --name safe-settings \
      -p 3000:3000 \
      -e APP_ID=123456 \
      -e WEBHOOK_SECRET=your-secret \
      -e PRIVATE_KEY="your-base64-key" \
      -e LOG_LEVEL=info \
      safe-settings
    

    Container Management

    View Logs

    # Follow logs in real-time
    docker logs -f safe-settings
    
    # View last 100 lines
    docker logs --tail 100 safe-settings
    
    # View logs since 10 minutes ago
    docker logs --since 10m safe-settings
    

    Connect to Running Container

    Access the container shell for debugging:
    docker exec -it safe-settings /bin/sh
    
    Inside the container:
    # Check node version
    node --version
    
    # View process
    ps aux
    
    # Check environment variables
    env | grep APP_ID
    
    # Exit
    exit
    

    Stop and Remove

    # Stop container
    docker stop safe-settings
    
    # Remove container
    docker rm safe-settings
    
    # Stop and remove in one command
    docker rm -f safe-settings
    

    Restart Container

    # Restart
    docker restart safe-settings
    
    # Restart with new environment variables
    docker rm -f safe-settings
    docker run -d --name safe-settings -p 3000:3000 --env-file .env safe-settings
    

    Environment Variables

    Create a .env file with all required variables:
    # GitHub App Credentials (Required)
    APP_ID=123456
    WEBHOOK_SECRET=your-webhook-secret-here
    PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMII...\n-----END RSA PRIVATE KEY-----"
    
    # Or use base64 encoded (recommended for Docker)
    PRIVATE_KEY="TUlJRXB...base64-encoded-key"
    
    # Application Settings
    NODE_ENV=production
    LOG_LEVEL=info
    PORT=3000
    
    # Safe Settings Configuration
    ADMIN_REPO=admin
    CONFIG_PATH=.github
    SETTINGS_FILE_PATH=settings.yml
    DEPLOYMENT_CONFIG_FILE=deployment-settings.yml
    
    # Optional: Scheduled Sync
    CRON='0 * * * *'  # Run every hour
    
    # Optional: GitHub Enterprise Server
    GHE_HOST=github.mycompany.com
    
    # Optional: Local Development
    WEBHOOK_PROXY_URL=https://smee.io/your-channel
    
    # Optional: SSL (use with caution)
    NODE_TLS_REJECT_UNAUTHORIZED=0
    
    Never commit .env files to version control. Add .env to your .gitignore file.

    Using Private Key File

    If you prefer using a private key file instead of environment variable:

    Modify Dockerfile

    Add this line before the CMD instruction:
    COPY private-key.pem /opt/safe-settings/.data/private-key.pem
    

    Update Environment Variable

    In your .env file:
    PRIVATE_KEY_PATH=/opt/safe-settings/.data/private-key.pem
    

    Rebuild Image

    docker build -t safe-settings .
    

    Production Deployment

    For production deployments:

    Use Docker Compose

    Create docker-compose.yml:
    version: '3.8'
    
    services:
      safe-settings:
        build: .
        container_name: safe-settings
        restart: unless-stopped
        ports:
          - "3000:3000"
        env_file:
          - .env
        logging:
          driver: "json-file"
          options:
            max-size: "10m"
            max-file: "3"
        healthcheck:
          test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/probot"]
          interval: 30s
          timeout: 10s
          retries: 3
          start_period: 40s
    

    Add Health Check

    Verify the container is healthy:
    docker inspect --format='{{.State.Health.Status}}' safe-settings
    

    Set Resource Limits

    Limit container resources:
    docker run -d \
      --name safe-settings \
      -p 3000:3000 \
      --env-file .env \
      --memory="512m" \
      --cpus="1.0" \
      --restart unless-stopped \
      safe-settings
    

    Enable Auto-Restart

    docker update --restart unless-stopped safe-settings
    

    Networking

    Expose to Internet

    For production, use a reverse proxy:

    Using Nginx

    server {
        listen 80;
        server_name safe-settings.example.com;
    
        location / {
            proxy_pass http://localhost:3000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
    

    Using Traefik

    Add labels to docker-compose.yml:
    services:
      safe-settings:
        # ... other config ...
        labels:
          - "traefik.enable=true"
          - "traefik.http.routers.safe-settings.rule=Host(`safe-settings.example.com`)"
          - "traefik.http.services.safe-settings.loadbalancer.server.port=3000"
    

    Docker Network

    Create a dedicated network:
    # Create network
    docker network create safe-settings-network
    
    # Run container in network
    docker run -d \
      --name safe-settings \
      --network safe-settings-network \
      -p 3000:3000 \
      --env-file .env \
      safe-settings
    

    Updating Safe Settings

    1
    Pull Latest Code
    2
    git pull origin main
    
    3
    Rebuild Image
    4
    docker build -t safe-settings .
    
    5
    Restart Container
    6
    docker-compose down
    docker-compose --env-file .env up -d
    

    Troubleshooting

    Container Won’t Start

    Check logs for errors:
    docker logs safe-settings
    
    Common issues:
    • Missing or invalid environment variables
    • Port 3000 already in use
    • Invalid private key format

    Port Already in Use

    Use a different port:
    docker run -d -p 8080:3000 --env-file .env safe-settings
    
    Update webhook URL to include the new port.

    Memory Issues

    Increase container memory:
    docker run -d --memory="1g" --env-file .env safe-settings
    

    Permission Errors

    The container runs as user node (UID 1000). If you have permission issues:
    # Check file ownership
    docker exec safe-settings ls -la
    
    # Fix ownership if needed (in Dockerfile)
    RUN chown -R node:node /opt/safe-settings
    

    Monitoring

    View Container Stats

    # Real-time stats
    docker stats safe-settings
    
    # One-time stats
    docker stats --no-stream safe-settings
    

    Export Logs

    # Export to file
    docker logs safe-settings > safe-settings.log
    
    # Export with timestamps
    docker logs -t safe-settings > safe-settings.log
    

    Health Check

    Manually check health:
    curl http://localhost:3000/probot
    
    Should return the Probot status page.

    Next Steps

    Configure Settings

    Set up your repository settings

    AWS Lambda

    Deploy to serverless

    Kubernetes

    Scale with Kubernetes

    GitHub Actions

    Scheduled sync with Actions

    Build docs developers (and LLMs) love