Self-Hosting EspoCRM with Docker Compose
What Is EspoCRM?
EspoCRM is a mature open-source CRM that handles contacts, accounts, leads, opportunities, email integration, calendar, and workflow automation. It’s been around since 2014 and has a significantly more polished UI than most open-source CRM alternatives. Think of it as a practical Salesforce replacement for small to mid-size teams — it does 80% of what Salesforce does at 0% of the cost.
Prerequisites
- A Linux server (Ubuntu 22.04+ recommended)
- Docker and Docker Compose installed (guide)
- 1 GB of free RAM (2 GB recommended for 5+ concurrent users)
- 5 GB of free disk space
- A domain name (required for email integration and WebSocket)
Docker Compose Configuration
EspoCRM needs four services: the main app, a background job daemon, a WebSocket server (optional but recommended), and MariaDB. All three EspoCRM services share the same Docker image with different entrypoints.
Create a docker-compose.yml:
services:
espocrm:
image: espocrm/espocrm:9.3.3
container_name: espocrm
depends_on:
espocrm-db:
condition: service_healthy
environment:
ESPOCRM_DATABASE_PLATFORM: Mysql
ESPOCRM_DATABASE_HOST: espocrm-db
ESPOCRM_DATABASE_USER: espocrm
ESPOCRM_DATABASE_PASSWORD: changeme_strong_password # CHANGE — must match DB
ESPOCRM_ADMIN_USERNAME: admin
ESPOCRM_ADMIN_PASSWORD: changeme_admin_password # CHANGE
ESPOCRM_SITE_URL: "http://localhost:8080" # CHANGE to your public URL
volumes:
- espocrm-data:/var/www/html # App files, config, uploads
ports:
- "8080:80" # Web UI
restart: unless-stopped
espocrm-daemon:
image: espocrm/espocrm:9.3.3
container_name: espocrm-daemon
entrypoint: docker-daemon.sh
volumes:
- espocrm-data:/var/www/html # Must share volume with main app
restart: unless-stopped
espocrm-websocket:
image: espocrm/espocrm:9.3.3
container_name: espocrm-websocket
entrypoint: docker-websocket.sh
environment:
ESPOCRM_CONFIG_USE_WEB_SOCKET: "true"
ESPOCRM_CONFIG_WEB_SOCKET_URL: "ws://localhost:8081"
ESPOCRM_CONFIG_WEB_SOCKET_ZERO_M_Q_SUBSCRIBER_DSN: "tcp://*:7777"
ESPOCRM_CONFIG_WEB_SOCKET_ZERO_M_Q_SUBMISSION_DSN: "tcp://espocrm-websocket:7777"
volumes:
- espocrm-data:/var/www/html # Must share volume with main app
ports:
- "8081:8080" # WebSocket for real-time updates
restart: unless-stopped
espocrm-db:
image: mariadb:11.7
container_name: espocrm-db
environment:
MARIADB_ROOT_PASSWORD: changeme_root_password # CHANGE
MARIADB_DATABASE: espocrm
MARIADB_USER: espocrm
MARIADB_PASSWORD: changeme_strong_password # CHANGE — must match app
volumes:
- espocrm-db-data:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 20s
start_period: 10s
timeout: 10s
retries: 3
restart: unless-stopped
volumes:
espocrm-data:
espocrm-db-data:
Before starting: Change all changeme passwords. Update ESPOCRM_SITE_URL to your actual access URL — this is critical for email links, calendar invites, and API responses.
docker compose up -d
Initial Setup
- Open
http://your-server-ip:8080in your browser - Log in with the admin credentials you set (
admin/ your password) - Complete the setup wizard — configure your company name, timezone, and date format
- Navigate to Administration → Integrations to connect email (IMAP/SMTP)
First Steps
| Step | Where | What to Do |
|---|---|---|
| 1 | Administration → Outbound Emails | Configure SMTP for sending emails |
| 2 | Administration → Personal Email Accounts | Connect IMAP for email tracking |
| 3 | Leads or Contacts | Import existing contacts from CSV |
| 4 | Administration → Roles | Set up user roles and permissions |
| 5 | Administration → Workflows | Create automation rules |
Key Features
EspoCRM ships with a comprehensive feature set out of the box:
| Module | What It Does |
|---|---|
| Contacts & Accounts | People and organizations with custom fields, relationships, activity history |
| Leads | Lead capture, scoring, and conversion to contacts/opportunities |
| Opportunities | Sales pipeline with stages, probability, and revenue forecasting |
| Calendar | Shared calendars, meeting scheduling, reminders |
| Full email integration (IMAP/SMTP), email-to-lead conversion, mass email | |
| Calls & Meetings | Log calls, schedule meetings, track outcomes |
| Workflows | Trigger-based automation (e.g., send email when deal stage changes) |
| Reports | Visual reports and dashboards with charts |
| Knowledge Base | Internal documentation and FAQ system |
| Stream | Activity feed showing all interactions per record |
Configuration
Email Integration
EspoCRM’s email features are one of its strongest selling points. To enable them:
- Outbound (SMTP): Administration → Outbound Emails → add your SMTP server
- Personal inbox: Click your avatar → Personal Email Accounts → add IMAP
- Group inbox: Administration → Group Email Accounts → add a shared inbox (e.g., [email protected])
Emails automatically link to contacts based on email address matching.
Custom Fields and Entities
EspoCRM’s entity manager lets you create custom record types and fields without writing code:
- Administration → Entity Manager
- Create a new entity or extend an existing one
- Add custom fields (text, number, enum, multi-enum, date, currency, and more)
- Define relationships between entities
Workflow Automation
Navigate to Administration → Workflows to create automation rules:
- Trigger types: After record created, after record updated, scheduled, sequential
- Actions: Send email, create record, update record, notify users, execute formula
- Conditions: Filter which records trigger the workflow
Example: automatically send a follow-up email 3 days after a deal enters “Proposal Sent” stage.
Reverse Proxy
For production with HTTPS, update your compose and reverse proxy configuration.
Update ESPOCRM_SITE_URL to your HTTPS domain:
ESPOCRM_SITE_URL: "https://crm.example.com"
For the WebSocket URL:
ESPOCRM_CONFIG_WEB_SOCKET_URL: "wss://crm.example.com/ws"
Caddy example (handling both HTTP and WebSocket):
crm.example.com {
reverse_proxy localhost:8080
handle /ws {
reverse_proxy localhost:8081
}
}
See our Reverse Proxy Guide for Nginx Proxy Manager and Traefik configurations.
Backup
Back up the database and application volume:
# Database dump
docker exec espocrm-db mariadb-dump -u root -p'your_root_password' espocrm > espocrm-backup-$(date +%Y%m%d).sql
# Application data (config, uploads, extensions)
docker run --rm -v espocrm-data:/data -v $(pwd):/backup \
alpine tar czf /backup/espocrm-data-$(date +%Y%m%d).tar.gz /data
Restore:
cat espocrm-backup-20260224.sql | docker exec -i espocrm-db mariadb -u root -p'your_root_password' espocrm
See our Backup Strategy Guide for automated approaches.
Troubleshooting
Scheduled Jobs Not Running
Symptom: Emails not being fetched, workflows not triggering, notifications not sent.
Fix: The espocrm-daemon service must be running. It handles all background jobs:
docker ps | grep espocrm-daemon
docker logs espocrm-daemon
The daemon must share the same volume as the main app (espocrm-data:/var/www/html). If the volume isn’t shared, the daemon can’t access the application code.
WebSocket Connection Failed
Symptom: Browser console shows WebSocket connection errors. UI doesn’t update in real-time.
Fix: Check that:
espocrm-websocketis runningESPOCRM_CONFIG_WEB_SOCKET_URLmatches how users access the WebSocket (includingws://vswss://for HTTPS)ESPOCRM_CONFIG_WEB_SOCKET_ZERO_M_Q_SUBMISSION_DSNuses the container name (tcp://espocrm-websocket:7777)- Port 8081 is accessible (or routed through your reverse proxy)
Email Sending Fails
Symptom: Outbound emails stuck in queue, no delivery.
Fix: Check SMTP settings in Administration → Outbound Emails. Test with the “Send Test Email” button. Common issues:
- Wrong port (use 587 for STARTTLS, 465 for SSL)
- Authentication required but not configured
- Firewall blocking outbound SMTP
ESPOCRM_SITE_URL Mismatch
Symptom: Login redirects loop, links in emails point to the wrong URL, API calls fail.
Fix: ESPOCRM_SITE_URL must exactly match the URL users type in their browser. If behind a reverse proxy with HTTPS, it must be https://your-domain.com — not http://localhost:8080. After changing, restart the container.
Resource Requirements
| Metric | Value |
|---|---|
| RAM (idle) | ~300 MB (app) + ~100 MB (daemon) + ~80 MB (websocket) + ~150 MB (MariaDB) |
| RAM (active, 5+ users) | 800 MB - 1.5 GB |
| CPU | Low-Medium |
| Disk | ~500 MB for application + database growth |
Verdict
EspoCRM is the most mature and feature-complete self-hosted CRM you can deploy today. The email integration is genuinely useful (not a checkbox feature), workflow automation covers real business processes, and the entity manager means you can customize it without touching code. It’s been actively developed since 2014 — the kind of stability that matters when you’re putting customer data into a system.
The main drawback is that the UI, while functional, isn’t as modern as newer alternatives like Twenty CRM. If you care more about a polished developer experience and GraphQL APIs, Twenty is worth looking at. But for raw CRM capability — lead management, email tracking, workflow automation, reporting — EspoCRM has years of refinement that newer tools haven’t matched yet.
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments