meetily

GitHub

Privacy first, AI meeting assistant with 4x faster Parakeet/Whisper live transcription, speaker diarization, and Ollama summarization built on Rust. 100% local processing. no cloud required. Meetily (Meetly Ai - https://meetily.ai) is the #1 Self-hosted, Open-source Ai meeting note taker for macOS & Windows.

RAW Compose
1. Pull Image docker pull library/meetily-backend
2. 1-Click Launch Command docker run -d -p 8178:8178 --name meetily meetily-backend:latest
3. Compose Deployment docker compose up -d
backend/docker-compose.yml
Download .yml
version: '3.8'

# ⚠️  AUDIO PROCESSING WARNING:
# Docker containers with insufficient resources will drop audio chunks when
# the processing queue becomes full (MAX_AUDIO_QUEUE_SIZE=10, lib.rs:54).
# Symptoms: "Dropped old audio chunk" in logs (lib.rs:330-333).
# 
# RECOMMENDED RESOURCE ALLOCATION:
# - Memory: 8GB minimum per service (16GB total recommended)
# - CPU: 2+ cores per service
# - Monitor logs for audio drop warnings during operation

services:
  # Original whisper-server service (Windows/Linux compatibility)
  whisper-server:
    build:
      context: .
      dockerfile: ${DOCKERFILE:-Dockerfile.server-cpu}
      args:
        CUDA_VERSION: ${CUDA_VERSION:-12.3.1}
        UBUNTU_VERSION: ${UBUNTU_VERSION:-22.04}
    image: whisper-server:${TAG:-cpu}
    container_name: whisper-server
    restart: unless-stopped
    
    # Port mapping
    ports:
      - "${WHISPER_PORT:-8178}:8178"
    
    # Environment variables
    environment:
      - WHISPER_HOST=0.0.0.0
      - WHISPER_PORT=8178
      - WHISPER_MODEL=${WHISPER_MODEL:-models/ggml-base.en.bin}
      - WHISPER_THREADS=${WHISPER_THREADS:-0}
      - WHISPER_USE_GPU=${WHISPER_USE_GPU:-true}
      - WHISPER_LANGUAGE=${WHISPER_LANGUAGE:-en}
      - WHISPER_TRANSLATE=${WHISPER_TRANSLATE:-false}
      - WHISPER_DIARIZE=${WHISPER_DIARIZE:-false}
      - WHISPER_PRINT_PROGRESS=${WHISPER_PRINT_PROGRESS:-true}
    
    # Volume mounts
    volumes:
      # Model storage - persistent across container restarts
      - whisper_models:/app/models
      # Upload directory for temporary files
      - whisper_uploads:/app/uploads
      # Optional: mount local models directory
      - ${LOCAL_MODELS_DIR:-./models}:/app/local_models:ro
      # Optional: mount custom configuration
      - ${CONFIG_DIR:-./config}:/app/config:ro
    
    # Exclude from macOS profile
    profiles:
      - default

  # macOS-optimized whisper-server service
  whisper-server-macos:
    build:
      context: .
      dockerfile: Dockerfile.server-macos
    image: whisper-server:macos
    container_name: whisper-server
    restart: unless-stopped
    
    # Port mapping
    ports:
      - "${WHISPER_PORT:-8178}:8178"
    
    # Environment variables (GPU disabled for macOS)
    environment:
      - WHISPER_HOST=0.0.0.0
      - WHISPER_PORT=8178
      - WHISPER_MODEL=${WHISPER_MODEL:-models/ggml-large-v3.bin}
      - WHISPER_THREADS=${WHISPER_THREADS:-0}
      - WHISPER_USE_GPU=false
      - WHISPER_LANGUAGE=${WHISPER_LANGUAGE:-en}
      - WHISPER_TRANSLATE=${WHISPER_TRANSLATE:-false}
      - WHISPER_DIARIZE=${WHISPER_DIARIZE:-false}
      - WHISPER_PRINT_PROGRESS=${WHISPER_PRINT_PROGRESS:-true}
      - WHISPER_PLATFORM=macos
    
    # Volume mounts for macOS (using actual models directory)
    volumes:
      # Map to actual model location on macOS
      - ./models:/app/models
      # Upload directory for temporary files
      - whisper_uploads:/app/uploads
      # Optional: mount custom configuration
      - ${CONFIG_DIR:-./config}:/app/config:ro
    
    # macOS profile only
    profiles:
      - macos
    
    # GPU support (uncomment for GPU version)
    # deploy:
    #   resources:
    #     reservations:
    #       devices:
    #         - driver: nvidia
    #           count: all
    #           capabilities: [gpu]
    
    # Health check
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8178/"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    
    # Resource limits (optional)
    mem_limit: 4g
    mem_reservation: 1g
    
    # Logging configuration
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  # Model downloader service - ensures models are downloaded before whisper server starts
  model-downloader:
    image: alpine/curl:latest
    container_name: whisper-model-downloader
    volumes:
      - whisper_models:/models
    environment:
      - MODEL_NAME=${MODEL_NAME:-base.en}
    command: |
      sh -c "
        echo '🔍 Checking for model: ggml-\${MODEL_NAME}.bin'
        if [ ! -f /models/ggml-\${MODEL_NAME}.bin ] || [ ! -s /models/ggml-\${MODEL_NAME}.bin ]; then
          echo '📦 Downloading model: \${MODEL_NAME}...'
          echo '📋 This may take a few minutes depending on model size and connection speed'
          # Create temp file to avoid partial downloads
          curl -L -f --progress-bar \
            --connect-timeout 30 \
            --max-time 3600 \
            --retry 3 \
            --retry-delay 5 \
            --retry-connrefused \
            -o /models/ggml-\${MODEL_NAME}.bin.tmp \
            https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-\${MODEL_NAME}.bin
          
          # Verify download completed successfully  
          if [ -s /models/ggml-\${MODEL_NAME}.bin.tmp ]; then
            mv /models/ggml-\${MODEL_NAME}.bin.tmp /models/ggml-\${MODEL_NAME}.bin
            echo '✅ Model downloaded successfully: \${MODEL_NAME}'
            ls -lh /models/ggml-\${MODEL_NAME}.bin
          else
            echo '❌ Download failed or file is empty'
            rm -f /models/ggml-\${MODEL_NAME}.bin.tmp
            exit 1
          fi
        else
          echo '✅ Model already exists: \${MODEL_NAME}'
          ls -lh /models/ggml-\${MODEL_NAME}.bin
        fi
        echo '🎉 Model preparation complete'
      "
    healthcheck:
      test: ["CMD", "test", "-f", "/models/ggml-${MODEL_NAME:-base.en}.bin"]
      interval: 10s
      timeout: 5s
      retries: 1
    profiles:
      - download

  # Meeting Summarizer Python App (Windows/Linux)
  meetily-backend:
    build:
      context: .
      dockerfile: Dockerfile.app
    image: meetily-backend:latest
    container_name: meetily-backend
    restart: unless-stopped
    
    # Port mapping
    ports:
      - "${APP_PORT:-5167}:5167"
    
    # Environment variables
    environment:
      - PYTHONUNBUFFERED=1
      - PYTHONPATH=/app
      - DATABASE_PATH=/app/data/meeting_minutes.db
      - OLLAMA_HOST=${OLLAMA_HOST:-http://host.docker.internal:11434}
    
    # Add extra host for Docker Desktop compatibility
    extra_hosts:
      - "host.docker.internal:host-gateway"
    
    # Volume mounts
    volumes:
      # Local database directory (created by setup-db.sh)
      - ./data:/app/data
      # Logs directory
      - meeting_app_logs:/app/logs
      # Optional: mount local .env file
      - ${LOCAL_ENV_FILE:-./app/.env}:/app/.env:ro
    
    # Health check
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5167/get-meetings"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    
    # Resource limits (optional)
    mem_limit: 2g
    mem_reservation: 512m
    
    # Logging configuration
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    
    # Depends on whisper server for transcription
    depends_on:
      whisper-server:
        condition: service_healthy
    
    # Exclude from macOS profile
    profiles:
      - default

  # Meeting Summarizer Python App (macOS)
  meetily-backend-macos:
    build:
      context: .
      dockerfile: Dockerfile.app
    image: meetily-backend:latest
    container_name: meetily-backend
    restart: unless-stopped
    
    # Port mapping
    ports:
      - "${APP_PORT:-5167}:5167"
    
    # Environment variables
    environment:
      - PYTHONUNBUFFERED=1
      - PYTHONPATH=/app
      - DATABASE_PATH=/app/data/meeting_minutes.db
      - OLLAMA_HOST=${OLLAMA_HOST:-http://host.docker.internal:11434}
    
    # Add extra host for Docker Desktop compatibility
    extra_hosts:
      - "host.docker.internal:host-gateway"
    
    # Volume mounts
    volumes:
      # Local database directory (created by setup-db.sh)
      - ./data:/app/data
      # Logs directory
      - meeting_app_logs:/app/logs
      # Optional: mount local .env file
      - ${LOCAL_ENV_FILE:-./app/.env}:/app/.env:ro
    
    # Health check
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5167/get-meetings"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    
    # Resource limits (optional)
    mem_limit: 2g
    mem_reservation: 512m
    
    # Logging configuration
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    
    # Depends on macOS whisper server for transcription
    depends_on:
      whisper-server-macos:
        condition: service_healthy
    
    # macOS profile only
    profiles:
      - macos

  # Optional: Web UI service (if you want a separate frontend)
  web-ui:
    image: nginx:alpine
    container_name: whisper-web-ui
    ports:
      - "${WEB_PORT:-80}:80"
    volumes:
      - ./web:/usr/share/nginx/html:ro
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - whisper-server
      - meetily-backend
    profiles:
      - web

# Named volumes for persistent data
volumes:
  whisper_models:
    driver: local
  whisper_uploads:
    driver: local
  meeting_app_logs:
    driver: local

# Networks (optional)
networks:
  default:
    name: whisper-network