Skip to main content
MadelineProto provides official Docker images optimized for production deployments. The Docker setup includes a custom-built PHP runtime with performance optimizations and required extensions.

Official Docker Image

MadelineProto’s Docker image is based on Debian Bookworm and includes:
  • Custom-compiled PHP 8.4+ with performance patches
  • jemalloc memory allocator for better performance
  • Required PHP extensions: igbinary, uv, memprof
  • FFmpeg for media processing
  • Optimized compiler flags for maximum performance

Quick Start

Basic Dockerfile

Create a Dockerfile for your bot:
FROM ghcr.io/danog/madelineproto:latest

WORKDIR /app

COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader

COPY . .

CMD ["php", "bot.php"]

Docker Compose Setup

Create a docker-compose.yml for a complete stack:
version: '3.8'

services:
  bot:
    build: .
    container_name: madelineproto-bot
    restart: unless-stopped
    depends_on:
      - mysql
    environment:
      - DB_TYPE=mysql
      - DB_HOST=tcp://mysql:3306
      - DB_NAME=madelineproto
      - DB_USER=root
      - DB_PASS=secret
      - TELEGRAM_API_ID=12345
      - TELEGRAM_API_HASH=your_api_hash
    volumes:
      - ./sessions:/app/sessions

  mysql:
    image: mysql:8.0
    container_name: madelineproto-db
    restart: unless-stopped
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - MYSQL_DATABASE=madelineproto
    volumes:
      - mysql_data:/var/lib/mysql

volumes:
  mysql_data:

Ephemeral Filesystems

When running in containers without persistent volumes, configure ephemeral filesystem mode:
use danog\MadelineProto\Settings;
use danog\MadelineProto\Settings\Database\Mysql;

$settings = new Settings;

$mysql = new Mysql;
$mysql->setDatabase(getenv('DB_NAME'));
$mysql->setUsername(getenv('DB_USER'));
$mysql->setPassword(getenv('DB_PASS'));
$mysql->setUri(getenv('DB_HOST'));

// IMPORTANT: Set ephemeral prefix for Docker deployments
$mysql->setEphemeralFilesystemPrefix(getenv('SESSION_PREFIX') ?: 'bot_1');

$settings->setDb($mysql);

MyEventHandler::startAndLoop('bot.madeline', $settings);
Critical: Never delete the session folder while the container is running. This will cause AUTH_KEY_DUPLICATED errors. Stop the container first, then delete the folder.

How Ephemeral Mode Works

  1. Session files are not used for persistent data
  2. All data is stored in the database with the specified prefix
  3. Each bot instance must have a unique prefix
  4. Safe to delete session folder when container is stopped

Multi-Container Setup

With PostgreSQL

version: '3.8'

services:
  bot:
    build: .
    restart: unless-stopped
    depends_on:
      - postgres
    environment:
      - DB_TYPE=postgres
      - DB_HOST=tcp://postgres:5432
      - DB_NAME=madelineproto
      - DB_USER=postgres
      - DB_PASS=secret
    volumes:
      - ./sessions:/app/sessions

  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=madelineproto
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

With Redis

version: '3.8'

services:
  bot:
    build: .
    restart: unless-stopped
    depends_on:
      - redis
    environment:
      - DB_TYPE=redis
      - REDIS_HOST=redis://redis:6379
      - REDIS_PASS=secret
      - REDIS_DB=0
    volumes:
      - ./sessions:/app/sessions

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --requirepass secret
    volumes:
      - redis_data:/data

volumes:
  redis_data:

Environment Variables

Configure your bot using environment variables:
// bot.php
use danog\MadelineProto\Settings;
use danog\MadelineProto\Settings\Database\Mysql;
use danog\MadelineProto\Settings\Database\Postgres;
use danog\MadelineProto\Settings\Database\Redis;

$settings = new Settings;

// App info from environment
$settings->getAppInfo()
    ->setApiId((int)getenv('TELEGRAM_API_ID'))
    ->setApiHash(getenv('TELEGRAM_API_HASH'));

// Database configuration
$dbType = getenv('DB_TYPE') ?: 'memory';

switch ($dbType) {
    case 'mysql':
        $db = (new Mysql)
            ->setDatabase(getenv('DB_NAME'))
            ->setUsername(getenv('DB_USER'))
            ->setPassword(getenv('DB_PASS'))
            ->setUri(getenv('DB_HOST'))
            ->setEphemeralFilesystemPrefix(getenv('SESSION_PREFIX'));
        break;

    case 'postgres':
        $db = (new Postgres)
            ->setDatabase(getenv('DB_NAME'))
            ->setUsername(getenv('DB_USER'))
            ->setPassword(getenv('DB_PASS'))
            ->setUri(getenv('DB_HOST'))
            ->setEphemeralFilesystemPrefix(getenv('SESSION_PREFIX'));
        break;

    case 'redis':
        $db = (new Redis)
            ->setUri(getenv('REDIS_HOST'))
            ->setPassword(getenv('REDIS_PASS'))
            ->setDatabase((int)getenv('REDIS_DB'))
            ->setEphemeralFilesystemPrefix(getenv('SESSION_PREFIX'));
        break;
}

if (isset($db)) {
    $settings->setDb($db);
}

MyEventHandler::startAndLoop('bot.madeline', $settings);

Performance Optimizations

The MadelineProto Docker image includes several optimizations:

jemalloc Memory Allocator

The image uses jemalloc instead of the default allocator:
ENV USE_ZEND_ALLOC=0
ENV LD_PRELOAD=/usr/lib/libjemalloc.so
This provides better memory management for long-running PHP processes.

Compiler Optimizations

PHP is compiled with aggressive optimization flags:
ENV PHP_CFLAGS="-fstack-protector-strong -fpic -fpie -O3 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -g"
ENV PHP_LDFLAGS="-Wl,-O1 -pie"

Required Extensions

Pre-installed PHP extensions:
  • igbinary: Fast binary serialization
  • uv: Async I/O event loop
  • memprof: Memory profiling for debugging
  • FFI: Foreign Function Interface
  • GMP: Arbitrary precision mathematics
  • intl: Internationalization
  • gd: Image processing

Health Checks

Add health checks to your Docker Compose setup:
services:
  bot:
    build: .
    healthcheck:
      test: ["CMD", "php", "-r", "echo 'OK';"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

Logging

Configure logging for Docker:
use danog\MadelineProto\Settings\Logger;

// Log to stdout for Docker logs
$settings->getLogger()
    ->setType(Logger::STDOUT_LOGGER)
    ->setLevel(Logger::LEVEL_VERBOSE);
View logs:
docker-compose logs -f bot

Scaling with Docker Swarm

Deploy multiple instances:
version: '3.8'

services:
  bot:
    build: .
    deploy:
      replicas: 3
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
    environment:
      - SESSION_PREFIX=${HOSTNAME}
Each replica must have a unique session prefix to avoid conflicts.

Building Custom Images

Extend the base image with your requirements:
FROM ghcr.io/danog/madelineproto:latest

# Install additional system packages
RUN apt-get update && apt-get install -y \
    imagemagick \
    && rm -rf /var/lib/apt/lists/*

# Install additional PHP extensions
RUN pecl install redis && docker-php-ext-enable redis

WORKDIR /app

COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader

COPY . .

CMD ["php", "bot.php"]

Production Deployment Checklist

1

Configure Database

Set up persistent database (MySQL, PostgreSQL, or Redis)
2

Enable Ephemeral Mode

Configure setEphemeralFilesystemPrefix() for stateless containers
3

Environment Variables

Use environment variables for sensitive configuration
4

Resource Limits

Set appropriate CPU and memory limits in Docker Compose
5

Logging

Configure logging to stdout for container log aggregation
6

Health Checks

Add health checks for container orchestration
7

Backup Strategy

Implement database backup procedures

Resource Limits

Set resource constraints:
services:
  bot:
    build: .
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 2G
        reservations:
          cpus: '1'
          memory: 1G

Troubleshooting

Container Keeps Restarting

  1. Check logs: docker-compose logs bot
  2. Verify database connectivity
  3. Ensure API credentials are correct
  4. Check for file permission issues

AUTH_KEY_DUPLICATED Error

  1. Stop all containers
  2. Clear session folder
  3. Ensure unique session prefixes for each instance
  4. Restart containers

Memory Issues

Increase container memory limits or optimize cache settings:
$settings->getDb()->setCacheTtl(60); // Reduce cache time

See Also

Build docs developers (and LLMs) love