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
- Session files are not used for persistent data
- All data is stored in the database with the specified prefix
- Each bot instance must have a unique prefix
- 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);
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
Configure Database
Set up persistent database (MySQL, PostgreSQL, or Redis)
Enable Ephemeral Mode
Configure setEphemeralFilesystemPrefix() for stateless containers
Environment Variables
Use environment variables for sensitive configuration
Resource Limits
Set appropriate CPU and memory limits in Docker Compose
Logging
Configure logging to stdout for container log aggregation
Health Checks
Add health checks for container orchestration
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
- Check logs:
docker-compose logs bot
- Verify database connectivity
- Ensure API credentials are correct
- Check for file permission issues
AUTH_KEY_DUPLICATED Error
- Stop all containers
- Clear session folder
- Ensure unique session prefixes for each instance
- Restart containers
Memory Issues
Increase container memory limits or optimize cache settings:
$settings->getDb()->setCacheTtl(60); // Reduce cache time
See Also