Skip to main content

Overview

SS-IMS includes a multi-stage Dockerfile for containerized deployments. This approach provides consistent environments across development, staging, and production.

Dockerfile Analysis

The application uses a multi-stage Docker build to optimize image size and security:
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS builder
WORKDIR /app

COPY *.csproj .
RUN dotnet restore

COPY . .
RUN dotnet publish -c Release -o out

FROM mcr.microsoft.com/dotnet/runtime:9.0 AS runtime
WORKDIR /app

COPY --from=builder /app/out .

ENTRYPOINT ["dotnet", "ss.Integrated.Management.Server.dll"]

Build Stages Explained

1

Builder Stage

Uses the full .NET 9.0 SDK to:
  • Restore NuGet dependencies (dotnet restore)
  • Build and publish the application in Release mode
  • Output compiled binaries to /app/out
2

Runtime Stage

Uses the smaller .NET 9.0 runtime image to:
  • Copy only the published output (not source code)
  • Reduce final image size by ~70%
  • Minimize attack surface for production deployments

Building the Docker Image

Prerequisites

  • Docker 20.10+ installed
  • Docker Compose 2.0+ (for multi-container setup)

Build Commands

1

Navigate to project directory

cd ss.Integrated.Management.Server
2

Build the image

docker build -t ss-ims:latest .
With a specific tag:
docker build -t ss-ims:v1.0.0 .
3

Verify the image

docker images | grep ss-ims

Running with Docker

Standalone Container

You must have a PostgreSQL database accessible from the container. The database cannot be localhost unless using host networking.
docker run -d \
  --name ss-ims \
  --env-file .env \
  --restart unless-stopped \
  ss-ims:latest

With Environment Variables

Inject environment variables directly:
docker run -d \
  --name ss-ims \
  -e DISCORD_BOT_TOKEN="your_token_here" \
  -e DISCORD_MATCHES_CHANNEL_ID="123456789" \
  -e DISCORD_REFEREE_ROLE_ID="123456789" \
  -e DISCORD_ADMIN_ROLE_ID="123456789" \
  -e DISCORD_GUILD_ID="123456789" \
  -e POSTGRESQL_CONNECTION_STRING="Host=postgres;Database=ss26db;Username=ss;Password=ss" \
  -e LANGUAGE="en" \
  --restart unless-stopped \
  ss-ims:latest

View Logs

docker logs -f ss-ims

Stop and Remove

docker stop ss-ims
docker rm ss-ims

Docker Compose Setup

For a complete deployment including PostgreSQL and Adminer:
1

Create docker-compose.yml

docker-compose.yml
version: '3.8'

services:
  postgres:
    image: postgres:15-alpine
    container_name: ss-postgres
    environment:
      POSTGRES_USER: ss
      POSTGRES_PASSWORD: ss
      POSTGRES_DB: ss26db
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./schema.sql:/docker-entrypoint-initdb.d/schema.sql
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ss -d ss26db"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  adminer:
    image: adminer:latest
    container_name: ss-adminer
    ports:
      - "8080:8080"
    depends_on:
      - postgres
    restart: unless-stopped

  ss-ims:
    build:
      context: ./ss.Integrated.Management.Server
      dockerfile: Dockerfile
    container_name: ss-ims
    env_file:
      - .env
    depends_on:
      postgres:
        condition: service_healthy
    restart: unless-stopped

volumes:
  postgres_data:
2

Create .env file

.env
DISCORD_BOT_TOKEN=your_token_here
DISCORD_MATCHES_CHANNEL_ID=123456789
DISCORD_REFEREE_ROLE_ID=123456789
DISCORD_ADMIN_ROLE_ID=123456789
DISCORD_GUILD_ID=123456789
POSTGRESQL_CONNECTION_STRING=Host=postgres;Database=ss26db;Username=ss;Password=ss;Port=5432
LANGUAGE=en
3

Download database schema

curl -o schema.sql https://gist.githubusercontent.com/Angarn/ddd9cf693aaeeb9407cc46b750fce3e5/raw/
4

Start all services

docker compose up -d
5

View logs

docker compose logs -f ss-ims

Service Access

  • Adminer: http://localhost:8080
  • PostgreSQL: localhost:5432
  • SS-IMS: Running in background, check logs for Discord connection status

Managing the Stack

docker compose up -d

Production Deployment Considerations

Security

Critical security practices for production:
  • Use Docker secrets or external secret management (e.g., HashiCorp Vault)
  • Never commit .env files to version control
  • Run containers as non-root user
  • Enable Docker Content Trust (DCT)
  • Scan images for vulnerabilities with docker scan

Non-Root User

Modify the Dockerfile to run as non-root:
Dockerfile
FROM mcr.microsoft.com/dotnet/runtime:9.0 AS runtime
WORKDIR /app

# Create non-root user
RUN useradd -m -u 1000 ssims

COPY --from=builder /app/out .

# Change ownership
RUN chown -R ssims:ssims /app

USER ssims

ENTRYPOINT ["dotnet", "ss.Integrated.Management.Server.dll"]

Resource Limits

Prevent resource exhaustion by setting limits:
docker-compose.yml
services:
  ss-ims:
    # ... other config
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
        reservations:
          cpus: '0.5'
          memory: 256M

Health Checks

Implement application health checks:
docker-compose.yml
services:
  ss-ims:
    # ... other config
    healthcheck:
      test: ["CMD", "dotnet", "--list-runtimes"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

Logging Configuration

Configure logging drivers for production:
docker-compose.yml
services:
  ss-ims:
    # ... other config
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
Or use a centralized logging solution:
docker-compose.yml
services:
  ss-ims:
    # ... other config
    logging:
      driver: "syslog"
      options:
        syslog-address: "tcp://logs.example.com:514"

Persistent Data

Always use named volumes for database data to prevent data loss during container updates.
volumes:
  postgres_data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /var/lib/ss-ims/postgres

Environment-Specific Configurations

Use multiple compose files:
version: '3.8'
services:
  ss-ims:
    build: ./ss.Integrated.Management.Server
    env_file:
      - .env
Deploy with:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Automatic Updates

For automated deployments with minimal downtime:
#!/bin/bash
# deploy.sh

set -e

git pull origin main
docker compose build ss-ims
docker compose up -d --no-deps ss-ims
docker image prune -f

Monitoring

Integrate with monitoring tools:
docker-compose.yml
services:
  prometheus:
    image: prom/prometheus:latest
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    ports:
      - "9090:9090"
  
  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    volumes:
      - grafana_data:/var/lib/grafana

volumes:
  prometheus_data:
  grafana_data:

Troubleshooting

Container Fails to Start

# Check logs
docker compose logs ss-ims

# Inspect container
docker inspect ss-ims

# Check environment variables
docker exec ss-ims env

Database Connection Issues

# Test PostgreSQL connectivity from SS-IMS container
docker exec ss-ims ping postgres

# Check if PostgreSQL is accepting connections
docker exec ss-postgres psql -U ss -d ss26db -c "SELECT 1;"

Discord Bot Not Connecting

1

Verify token

docker exec ss-ims env | grep DISCORD_BOT_TOKEN
2

Check network connectivity

docker exec ss-ims ping discord.com
3

Review application logs

docker logs ss-ims 2>&1 | grep -i discord

Image Build Failures

# Build with verbose output
docker build --progress=plain -t ss-ims:latest .

# Build without cache
docker build --no-cache -t ss-ims:latest .

Build docs developers (and LLMs) love