When a Docker Compose EspoCRM deployment fits production
Docker Compose is a practical production option for EspoCRM when your team can operate a small multi-container stack and wants the same build on every environment. The open-source edition can be downloaded and self-hosted for free, and its source is licensed under AGPL-3.0, so the main costs are the infrastructure you run it on, the people who operate it, and any outside help you buy to set it up. Before choosing this path, confirm that someone on your team can own image updates, database backups, certificate renewal and container logs, because Compose gives you a repeatable definition but does not operate the stack for you.
The stack has four moving parts. The PHP application container serves the web interface and API. The database container holds all CRM records, and the EspoCRM server configuration lists MySQL 8.0 or newer, MariaDB 10.6 or newer, or PostgreSQL 15 as supported choices. Background jobs such as scheduled emails, workflows and imports need cron or an equivalent scheduled task, so a separate daemon or scheduler container is the usual way to keep that work running alongside the web tier. The WebSocket container is optional and only needed if you want real-time updates. Confirm the exact image tags, PHP version and database choice against the current server configuration documentation before you write the file, since the supported ranges are version-specific.
Compose fits best when your platform team already runs containers, wants version-controlled infrastructure and can restore the stack from backups on a schedule. Prefer the installation script when you want a single Ubuntu, Debian or Linux Mint host with a quick, opinionated setup, and it includes a command.sh utility for backups, restores and upgrades. Prefer a bare-metal or IIS setup when your standards do not allow containers, or when your hosting platform already standardizes on Apache, Nginx or IIS. Illustrative example: a team with an existing container host and a database platform it already backs up will usually find Compose easier to audit than a hand-built server.
Which Compose layout fits your team
A single-host pilot runs the application, database, background job and WebSocket containers on one machine that your team can reach from a test network. It is the quickest way to confirm that EspoCRM starts, that your custom entities and layouts behave as expected and that users can work through the interface before you commit to production hosting. Treat the pilot as a learning environment with a defined exit, because its Compose file and volumes are often carried forward into production.
- Ownership: your team builds and maintains the Compose file, since no outside operator runs the pilot unless you contract one in writing.
- Data: confirm that the pilot holds only test or sanitized records, because pilots frequently grow into production without a formal review.
- Access: confirm that the instance is reachable only from the networks you choose and that administrator accounts are not used for daily work.
- Database version: confirm that the MariaDB, MySQL or PostgreSQL image you select meets the versions listed in the server configuration documentation.
- Background jobs: confirm that the scheduled job process is running, because email, workflow and import tests fail quietly when it is not.
Check that the pilot's secrets, volumes and environment settings are written so they can move to the production host without a rewrite.
This layout suits an organization that already operates a reverse proxy, load balancer or certificate process on shared infrastructure. Your platform team keeps ownership of TLS, public DNS and firewall rules, while the EspoCRM containers run on a private network behind that proxy. The Compose file publishes only the port the proxy needs, and the proxy handles certificate renewal, HTTP to HTTPS redirects and the forwarded headers the application relies on.
- Ownership: your infrastructure team owns the host, proxy, certificates and backups. DeployOpen can implement the stack and write its runbook if the scope is agreed in writing.
- Proxy settings: confirm which proxy you run, that it forwards to the application container and that upload size and timeout limits match your largest expected attachments.
- Certificates: confirm who renews certificates and how a failed renewal raises an alert, because an expired certificate stops every user at the same moment.
- Secrets: confirm that database passwords and application secrets come from Docker secrets or your secret manager, not from files stored in the repository.
- Backups: confirm that database dumps and the files volume are both backed up and that a restore has been tested on a separate host.
Check that the proxy configuration, the WebSocket path if you enable live updates and the settings in the EspoCRM security documentation are reviewed together before go-live.
EspoCRM provides an installation script for Ubuntu, Debian and Linux Mint. It deploys a Docker-based stack with Nginx and MariaDB, can request Let's Encrypt certificates and installs a command.sh utility for backups, restores and upgrades. This is the shortest route to a working instance on one Linux server, and it suits a team that accepts the project's stack choices in return for less initial setup.
- Operating system: confirm that your server runs one of the supported distributions, since the script is written for them.
- Stack choices: confirm that Nginx and MariaDB meet your internal standards, because changing either later means moving to a custom Compose layout.
- Certificates: confirm public DNS records and inbound port access before you enable Let's Encrypt, since issuance depends on both.
- Operations: confirm who runs command.sh backups and restores and where backup copies are stored off the host.
- Upgrades: confirm the upgrade procedure in the release notes first, because upgrading through the UI is disabled by default as of version 10.0.
Check the installation script documentation for the current script version and the steps for your distribution before you run it on a host that will hold data.
Configuration options and when each fits
This table compares the database, the layer in front of the container, how background jobs run, the WebSocket daemon and secrets handling. Read each row as a decision: the trade-off names what you gain and what you take on, and the confirm column lists what to check in the vendor docs before you commit.
| Decision | Option and trade-off | What to confirm with vendor docs |
|---|---|---|
| Database: MariaDB 10.6 or newer | Matches the bundled MariaDB pattern in the official install script. Lowest adoption friction, and the stack is familiar to most Linux operators. | Exact supported minor versions for your EspoCRM release and any Compose image tag you pin. |
| Database: MySQL 8.0 or newer | Supported by the server configuration docs. Suits teams already running MySQL backups and monitoring, but you must align image choice with your backup tooling. | Version floor and character set or collation settings required by your release. |
| Database: PostgreSQL 15 | Supported by the server configuration docs. Fits organizations standardizing on PostgreSQL, though it departs from the stack the install script deploys. | Whether your release and extensions are tested against PostgreSQL 15, and how to migrate if you switch databases later. |
| Proxy: bundled Nginx from the install script | Fastest path to a working stack with optional Let's Encrypt. You own the TLS renewal and hardening afterward. | Which Nginx settings the script writes and which you must adjust for upload size and internal path denial. |
| Proxy: your existing Nginx, Apache or IIS in front | Reuses your edge standards, certificates and monitoring. Requires a manually written server block and proxy headers. | Required PHP modules and rewrite rules for your web server, and the WebSocket proxy configuration if you enable it. |
| Background jobs: cron inside the stack | Keeps scheduling with the containers, so it moves with the stack. Needs a dedicated daemon or cron container that runs the job loop. | The exact command the official Docker setup runs for scheduled jobs and whether it needs its own container. |
| Background jobs: cron on the host | Uses existing host tooling and logs. Risks drift if the container image changes and the host schedule is not documented alongside the Compose file. | Which container path and user the job must execute as, and the required interval. |
| WebSocket daemon: enabled in Compose | Gives real-time updates without a separate host service. Adds a container to monitor and a proxy route to publish. | The ZMQ extension requirement and the Docker configuration keys for the daemon. |
| WebSocket daemon: disabled | Simplest stack with fewer moving parts. Users refresh to see changes made by others. | Whether any feature you rely on depends on real-time notifications. |
| Secrets: environment variables in the Compose file | Quick to set and easy to review. Values are visible to anyone who can read the file or inspect the container. | Which settings accept environment variables and how the docs recommend storing database passwords. |
| Secrets: Docker secrets files | Keeps credentials out of the Compose file and container inspection output. Requires file mounts and a process for rotation. | Whether your image reads secrets from files and the expected file path or variable naming. |
Setup sequence with checks for a production Compose stack
This sequence takes a host from bare metal or a virtual machine to a running EspoCRM instance with persistent data, working background jobs, a confirmed real-time connection and tested backups. Each step ends with a check so the next step starts from a known state.
Prepare the host and storage volumes
The platform or infrastructure team installs a supported Docker Engine and the Compose plugin on a host they control, creates a dedicated directory for the stack, and places the database and EspoCRM data on storage with a defined backup scope. Confirm which PHP and database versions your chosen EspoCRM release requires by checking the server configuration documentation before choosing images. Done means the host is patched, time synchronization is on, firewall rules expose only the reverse proxy ports, and a test container can write to each named volume or bind mount.
Provision database credentials as Docker secrets
The administrator creates separate database users for the application and for maintenance tasks, generates strong passwords outside the Compose file, and stores them as Docker secrets or files with restricted permissions. The database container should read its root and application passwords from those secret files rather than from environment variables in version-controlled files. Done means the Compose file contains no plaintext passwords, the secret files are readable only by the service account, and a rotation procedure is written down.
Configure environment settings and reverse proxy
The implementer sets the site URL, timezone, database host and name, and the public hostname in the environment file, then defines how TLS terminates at the reverse proxy and which headers it forwards. Keep the EspoCRM container off public ports and confirm the proxy forwards the original scheme and host. Done means
docker compose configrenders without errors, no secret values appear in its output, and the environment file has restricted permissions.Start the containers and confirm persistence
The team brings up the application, database, daemon and WebSocket services with
docker compose up -d, then checks container health and logs for startup errors. Restart the stack and confirm that the database and the EspoCRM data and custom directories still contain their files, because a volume mounted in the wrong place will look healthy until the first restart. Done means every service reports running, the logs show no permission or connection errors, and the data survives a fulldocker compose downandupcycle.Complete the installation and lock down admin access
The administrator completes the web installer through the public hostname, creates a named administrator account, and then creates a separate account for daily work so the administrator does not use the admin role for routine tasks. Set the password policy and require two-factor authentication before users are invited, and review the security documentation for session lifetime and IP restriction options. Done means the installer is closed, the admin account is not used for daily work, and a test login with the non-admin account succeeds.
Verify background jobs run on schedule
The operator confirms that the cron entry or scheduled job for EspoCRM runs inside the stack, not only on the host, and that the container user can execute it. Queue a test job from the administration area or create a test record that triggers an asynchronous action, then check the job log. Done means the scheduled job executes at the expected interval, the job log shows completed runs without errors, and the check is repeated after a container restart.
Confirm the WebSocket connection through the proxy
The implementer opens EspoCRM in two browser sessions under different users, triggers a change that should notify the other session, and confirms the update arrives without a page reload. If it does not, check that the WebSocket daemon is running and that the reverse proxy forwards the upgrade headers for the WebSocket path. Done means a notification appears in the second session, and the daemon restarts cleanly after a stack restart.
Test backup and restore before users arrive
The operator runs a full backup covering the database dump and the EspoCRM data and custom files, stores it outside the host, and restores it to a separate test stack built from the same Compose file. Confirm that logins, records and attachments work in the restored copy and that the restore procedure is documented with the commands used. Done means a restore has been completed successfully at least once, the backup schedule and retention are set, and the team knows who runs recovery.
How secrets, volumes and configuration keep data safe across restarts
The most important design decision in a Compose deployment of EspoCRM is that the database files and uploaded attachments live outside the container lifecycle. Containers are disposable: replacing the application image, recreating a container after a configuration change, or rebuilding a host should never be the event that deletes CRM records or customer documents. Put the MariaDB or MySQL data directory on a named volume or a bind mount on storage you back up, and do the same for the EspoCRM data directory that holds attachments and generated files. Confirm the exact container paths and which directories EspoCRM expects to persist with the EspoCRM docs on server configuration before you write the volume section of your Compose file, because the application code itself is meant to come from the image, not from a volume.
When you replace an image, Compose recreates the container but reattaches the same volumes, so records and files survive. What does not survive is anything written into the container's writable layer, such as a manual edit to a PHP file or a hand-installed extension. Treat that as a risk to plan for: customizations should go through supported mechanisms, and your change record should say where each one lives. Before any image upgrade, take a database dump and a copy of the attachment volume, and keep the previous image tag so you can roll back. Test that restore path on a separate host, since a backup you have never restored is an assumption rather than a control.
Database credentials and application keys should not sit in a committed Compose file. Supply them through Docker secrets mounted as files, or through an environment file kept out of version control with restrictive file permissions, and confirm which settings EspoCRM and the database image read from *_FILE variants or from the environment in your image version. Restrict who can read the host directory that holds the secrets, rotate the database password through a planned change that updates both the database and the application side, and record where the application key is stored, because losing or changing it can affect encrypted values and sessions. Review the EspoCRM security docs for the related session and password controls that depend on the same configuration.
Security settings to apply before users log in
A new EspoCRM instance is reachable from the network before anyone signs in, so the security settings belong in the first build, not a later review. The cards below follow the EspoCRM security guidance and cover the account, session and network controls to set before the first user account is issued.
Enforce a password policy
Set the password policy in the administration settings before creating user accounts, so every password meets your minimum length and complexity rules from the first login. Check that the policy applies to accounts created by import and API users as well as those created in the interface. If you plan to use OpenID Connect or LDAP for sign-in, confirm which accounts still rely on local passwords, since the policy only protects those.
Require two-factor authentication
Turn on two-factor authentication for users so a stolen password alone does not grant access to customer records. Note that EspoCRM's own 2FA is not compatible with OpenID Connect, so organizations using single sign-on must enforce MFA at the identity provider instead. Decide this before rollout, because changing the sign-in method later affects every account.
Keep admins out of daily work
Create a separate administrator account for configuration tasks and give day-to-day users roles that cover only their sales, service or marketing work. Administrators who also send email and edit records expose the whole instance to phishing and session theft. Review the admin list during go-live and again after each staff change.
Limit authentication token lifetime
Shorten the lifetime of authentication tokens so that a token left on a shared workstation expires within a defined window. Agree the value with the security team and document it, because it trades convenience for exposure. Confirm that the chosen lifetime also applies to sessions used through the API and mobile access.
Disable password recovery for admins
Turn off password recovery for administrator accounts so that a reset email cannot grant administrator access to anyone who controls a mailbox. Plan how an administrator regains access if locked out, such as a second administrator or a documented recovery procedure that runs through your infrastructure team. Test that recovery works before go-live, not during an incident.
Restrict access by IP address
Use the IP allowlist in EspoCRM, and your reverse proxy or firewall, to limit who can reach the login page and the administration area. Remember that a reverse proxy can hide the client address unless forwarded headers are configured correctly, so test the allowlist from inside and outside your network. Keep the list short and record who owns each entry.
Maintenance commands and day-two tasks
Does EspoCRM include a command for backups and upgrades?
Yes, but only on the installation-script path. The EspoCRM installation script for Ubuntu, Debian and Linux Mint deploys a Docker-based stack with Nginx and MariaDB, and it includes a command.sh utility for backups, restores and upgrades. If your team built its own Compose file, as this topic describes, that utility may not exist in your setup, so you will need a documented procedure for each of those three tasks. Before you rely on any subcommand, read the script and the installation documentation, then run each task in a test environment. Confirm the exact options, what each task covers (database, files, configuration) and where its output is written.
How often should I back up the EspoCRM database and files?
The backup frequency should follow how much recent data your sales and service teams can afford to lose and how long a restore will take. Back up the database on that schedule, and back up the files that hold uploaded attachments and configuration as well. A complete set usually includes:
- a database dump taken while the stack is running or in a maintenance window
- the uploaded files and any mounted data volumes
- the environment files, reverse proxy configuration and Docker secrets, stored in a secure vault
Store copies off the host that runs the stack, encrypt them, and record how long each copy is kept. Ask your compliance owner to confirm the retention period before you set the schedule.
How do I test an EspoCRM restore without touching production?
Restore into a separate host or a separate Compose project with its own volumes, then check that the instance works before you count the backup as usable. A restore test is complete only when someone has verified it. Check at least these points:
- administrators can sign in, and the expected users, roles and teams exist
- record counts and recent records match the source for sample entities
- attachments open correctly from the restored file storage
- scheduled jobs run after the restore, and email and workflows behave as expected
Record the time each step took. That result gives you a realistic basis for recovery planning, and it shows whether your runbook is complete. Run this test on a schedule, not only after the first install.
How do I update EspoCRM and its containers safely?
Plan each update as a change with a backup taken immediately beforehand. Upgrading through the EspoCRM user interface is disabled by default as of version 10.0, so use the command-line process that matches your installation method. Pin image tags instead of using a floating tag, read the release notes for every version between your current release and the target, and test the upgrade on a restored copy. Check compatibility for your PHP version, database version and any installed extensions. Keep the previous image available so you can roll back if the test fails. Confirm the sequence in the EspoCRM releases page and the server documentation before scheduling any production change.
How do I confirm that scheduled jobs are running?
Check that the job runner is active and that queued work is being processed. EspoCRM needs cron, or a scheduled task on Windows, for background jobs, so the Docker stack must run the equivalent job runner continuously. Look at the scheduled job records in the administration area for failed or stalled entries, and review the runner's container logs. When the runner stops, email delivery, workflows, formula-driven updates and imports can pause without any visible error on the user screen. Add a simple external check that alerts someone when the runner stops. The server configuration documentation lists the requirements to verify against your installed version.
Which logs should I review on a running EspoCRM stack?
Review the application log, the web server access and error logs, and the container logs for the database, the application, the background job runner and the WebSocket service. Focus on patterns rather than single lines. Repeated failed sign-ins, permission errors, database connection failures, and job errors that recur at the same time each day usually matter most. Forward logs to a central system if your security team requires retention or alerting. Confirm the log locations, rotation settings and retention period in your own environment, because they depend on how the stack was built. Restrict who can read logs, since they can contain personal data from customer records.
What should a partner include when quoting day-two maintenance?
Ask for a written scope that names each recurring task, who performs it, how often, and what evidence you receive. The main cost drivers are the number of instances and environments, the backup and retention design, the amount of restore testing, the frequency of upgrades, and the extensions that must be checked at each release. Ask whether the work is a one-time runbook delivery or an ongoing retainer. DeployOpen plans, documents and supports self-hosted platforms in your own infrastructure, and most engagements are fixed-scope with agreed deliverables and timelines. Request a sample runbook and a restore test report before you sign.
Limits to confirm in your environment
A Docker Compose stack for EspoCRM inherits constraints from the release, the database engine, the extensions you install and the authentication model you choose. The cards below list what to confirm against the official docs for your target version before anything is provisioned, since a mismatch found after go-live is far more expensive to correct.
PHP and database versions
The server configuration docs list PHP 8.3 to 8.5 and MySQL 8.0 or newer, MariaDB 10.6 or newer or PostgreSQL 15. Confirm the exact combination for the release you plan to run, because the image tags you pin must match it. Check this again before any major upgrade, since supported ranges change between versions.
Compose layout versus the installer
EspoCRM's installation script deploys a Docker-based stack with Nginx and MariaDB, plus a command.sh utility for backups, restores and upgrades. A hand-written Compose file may not use the same service names, volume paths or helper commands. Decide early whether you will follow the script layout or maintain your own, and document which backup and upgrade procedure applies.
Background jobs and WebSocket setup
Background jobs require a scheduled process, and real-time notifications depend on a separate WebSocket daemon that needs the ZMQ extension and a reverse proxy path to it. Confirm that your Compose services run the job runner and the WebSocket process as separate containers, that they share the application volume, and that the proxy forwards upgrade requests correctly.
Extension compatibility
Official extensions such as the Advanced Pack, which provides workflows and reports, and the Sales Pack are released against specific EspoCRM versions. Before choosing a release, confirm that every extension you plan to install supports it. Install extensions in a staging copy first, because a failed extension install during an upgrade can leave the instance in a partial state.
OIDC and built-in 2FA conflict
EspoCRM supports OpenID Connect sign-in with optional user creation on first login, group-to-team mapping and backchannel logout. The official docs state that EspoCRM's own two-factor authentication is not compatible with OIDC, so the identity provider must enforce the second factor for SSO users. Plan the fallback login path for administrators before enabling OIDC in production.
Release timing for the target version
EspoCRM 10.0.0 was released on July 2, 2026, and 10.0.10 followed on October 14, 2026. Upgrading through the UI is disabled by default as of version 10.0, so plan upgrades through the CLI or the command utility. Use these dates only as release context, and check the releases page for the current patch level before you pin an image.
What a production EspoCRM Compose deployment costs and requires
Is EspoCRM free to run on our own servers?
Yes, the open-source edition can be downloaded and self-hosted for free, according to the EspoCRM pricing page, and its source is licensed under AGPL-3.0 according to the license metadata of the espocrm/espocrm repository. Your costs then sit elsewhere: the servers or cloud accounts that host the containers, backup storage, the time your team or a partner spends on setup, and any official extensions you add. Confirm the license terms with your legal team before you modify the code and distribute it, because AGPL-3.0 has obligations that apply when software is offered over a network.
- Infrastructure you pay for each month
- One-time implementation effort
- Recurring upgrades, backups and monitoring
How do EspoCRM Cloud plan prices compare with self-hosting?
EspoCRM Cloud is a hosted subscription, so its cost is per user and does not include your own infrastructure. According to the EspoCRM pricing page, the Basic plan costs 15 US dollars per user per month with a minimum of 3 users, Enterprise costs 25 dollars with a minimum of 5 users, and Ultimate costs 69 dollars with a minimum of 10 users. The Advanced Pack is included in all cloud plans. Compare these figures with your own hosting, staff time and maintenance effort, using the same user count and the same feature set, because a self-hosted instance without the Advanced Pack does not provide the same workflows and reports.
What drives the cost of a production Compose deployment?
The largest drivers are the number of users and integrations, the volume of data to migrate, the amount of customization, and the security controls your organization requires. Ask for a quote built from a written scope that lists each of these items. Higher costs usually come from custom entities and formula logic, SSO with identity provider group mapping, email and telephony connections, data migration from older systems, and evidence for security reviews. Lower costs come from a narrow first release, standard layouts and a clear list of what is excluded. Most engagements are fixed-scope, so the quote should name deliverables, timelines and acceptance criteria before work starts.
How long does a production EspoCRM deployment take?
Duration depends mainly on the integrations and the data, not on the containers themselves. A basic Docker Compose stack with a database, the application and background processes can be built quickly, while SSO, email, data migration and custom entities each add review cycles with your teams. The EspoCRM installation docs describe a script for Ubuntu, Debian and Linux Mint that deploys a Docker-based stack, but a production build still needs TLS, backups, secrets handling and testing. Ask for a schedule that separates a technical baseline from business configuration, and confirm which phases depend on your identity provider, mail system and data exports.
What does the work need from our team?
Your team needs to provide a named business owner who decides how sales and service processes map to CRM records, an infrastructure contact who controls DNS, certificates, servers or cloud accounts, and an identity contact if you use SSO or LDAP. You also need access to the current data sources, sample exports for migration testing, and a mail administrator for sending and receiving addresses. Plan time for reviewing layouts, roles and sample records, since most rework comes from unclear ownership of fields and permissions rather than from the software.
Can an internal administrator run EspoCRM after go-live?
Yes, a trained administrator can run day-to-day tasks such as users, roles, layouts and reports, provided the platform team owns the container host, backups and upgrades. The EspoCRM security docs advise against using admin accounts for daily work, so separate admin and standard accounts from the start. Ask whether your administrator can rebuild indexes, review background jobs and restore a backup in a test environment. If not, budget for a support retainer or an internal training plan before the handover.
What recurring maintenance does a Compose deployment need?
Recurring work includes applying security and minor updates, testing backups and restores, rotating secrets and certificates, reviewing logs and background job health, and reviewing user access. EspoCRM 10.0.0 was released on July 2, 2026 and 10.0.10 on October 14, 2026, according to the espocrm/espocrm releases page, so plan for a regular patch cycle. The docs note that upgrading through the UI is disabled by default as of version 10.0, so upgrades run through a planned command-line process. Confirm your patch cadence, the test environment you will use and who approves changes before go-live.
How DeployOpen scopes and supports EspoCRM deployments
DeployOpen plans, implements, hardens, backs up, documents and supports self-hosted open-source platforms inside your own cloud accounts, data centers or air-gapped networks. For EspoCRM running in production with Docker Compose, we work from a written scope that fixes what is built and handed over before any work starts.
How we scope
- We confirm the EspoCRM version, PHP and database choice, and the host or hosts that will run the Compose stack.
- We record which containers, volumes, secrets and maintenance routines the written scope includes and excludes.
- We agree the reverse proxy, TLS approach, backup targets and any WebSocket or external storage needs before build starts.
How we build
- We write the Compose file for the application, database, daemon and WebSocket containers with pinned image versions.
- We configure environment variables and Docker secrets so credentials stay out of the Compose file and shell history.
- We test restarts, volume persistence, cron-driven background jobs, backup and restore before the stack is accepted.
How we hand over
- You receive the Compose files, environment templates, runbooks and an upgrade and rollback procedure written for your team.
- We walk your operators through backup, restore, maintenance commands and how to check that scheduled jobs are running.
- Written deliverables and acceptance criteria are recorded in the scope so both sides can confirm completion.
How we support
- Ongoing support, upgrades and continuous testing are available as retainers once the stack is in production.
- Retainer work can cover planned EspoCRM patch and minor updates, container image refreshes and restore rehearsals.
- Major version moves are scoped separately, because extensions, customizations and PHP requirements must be checked first.

