When storing EspoCRM attachments in Amazon S3 makes sense
Move EspoCRM attachments to object storage when uploaded files have started to dominate the application server's disk, when backups and restores take longer because of file volume, or when your policy requires file data to sit in a managed, replicable store. Attachments are the files users upload to records, such as documents on accounts, emails and notes. The database holds only the metadata, so the awsS3Storage configuration changes where the file bytes live, not how EspoCRM stores records. Your team can then choose between Amazon S3 in an AWS account you control and an S3-compatible service you run yourself, such as Ceph, provided that service supports the API features EspoCRM calls.
Before committing, decide which storage model fits your operations. Object storage moves file durability and capacity planning to the storage platform, but it adds a new dependency that must be reachable from the EspoCRM containers or hosts, a bucket with its own access policy, and a credential lifecycle to manage. The Docker and web server setup in your deployment stays the same, since EspoCRM still runs on the same application and PHP layers. Plan to test restores that include both the database and the bucket, because a database backup without the matching objects leaves attachment records pointing at nothing.
Treat the exact configuration keys, the supported options and any version-specific behavior as items to confirm, not facts to assume. Check the EspoCRM server configuration documentation for the version you run, EspoCRM server configuration docs, and compare its listed awsS3Storage options against your bucket, endpoint and addressing requirements. Confirm with your storage vendor how it expects path-style or virtual-hosted requests before you set anything. Then run a staging test that uploads, downloads, previews and deletes an attachment, and document that result as the acceptance evidence for the change.
Which S3 configuration options to decide before you start
This table compares the storage settings you need to decide before touching EspoCRM, with the situations each choice fits and what to confirm with your storage team. Read each row as a question to answer for your environment, then check the exact setting names against the documentation for the EspoCRM release you run. The main takeaway is that endpoint, addressing style and credential source usually decide whether the integration works with non-AWS storage.
| Decision | Options and when they fit | What to confirm with the storage team |
|---|---|---|
| Bucket | A dedicated bucket used only for EspoCRM attachments, so access policies and lifecycle rules apply to CRM files alone | Naming rules, whether the bucket exists before install, and who owns its policy |
| Region | The AWS region where the bucket lives. For S3-compatible services, the region value the service expects, which may be a placeholder | Exact region string the service accepts and whether it must match the bucket location |
| Endpoint | Blank or the default for AWS. Set a custom endpoint for MinIO-style, Ceph-style or other private or air-gapped object stores | Full endpoint URL, whether it uses HTTPS, and the certificate authority EspoCRM's server must trust |
| Path-style addressing | Usually off for AWS virtual-hosted buckets. Often needed on self-hosted stores that do not support bucket subdomains | Whether the service requires path-style requests and whether DNS for bucket subdomains exists |
| Credential source | Environment variables injected at runtime for static keys, or an instance role or workload identity on AWS where the host supports it | Who issues keys, rotation schedule, and whether the key can be limited to one bucket |
| Access policy | Least-privilege permissions covering only the object actions EspoCRM performs on attachments | A written list of required actions, verified against the release in use before production |
| Network path | Public endpoint for cloud buckets, or a private route, VPC endpoint or internal DNS name for controlled environments | Firewall rules from the EspoCRM host, DNS resolution inside the network, and egress approval |
| Config location | Settings kept in the application configuration file, with secrets referenced from the environment rather than stored in plain text | Where secrets are stored, who can read the host environment, and how backups handle the config file |
Choosing AWS S3 or a self-hosted S3-compatible backend
When EspoCRM runs on AWS compute, the bucket is usually a native AWS S3 bucket in the same account and region. The endpoint is normally resolved by the AWS SDK from the region, so you often leave the custom endpoint empty and avoid path-style addressing unless you have a specific reason to set it. Access should come from an IAM role attached to the instance or task rather than long-lived keys, and the policy should allow only the object actions EspoCRM needs on the attachment prefix. Confirm the exact settings with the EspoCRM server configuration documentation for your release.
- Endpoint and addressing: Leave the custom endpoint unset for standard AWS regions and confirm the region value matches the bucket.
- Network path: Decide whether traffic reaches S3 over public endpoints or through a VPC gateway endpoint, and check that security groups and route tables allow it.
- TLS trust: Public AWS certificates are trusted by default, so no custom CA bundle is normally needed.
- Backup responsibility: Enable versioning and decide on cross-region replication or AWS Backup with your own retention rules, since AWS does not back up your data for you by default.
- Access model: Prefer instance or task roles and keep keys out of the EspoCRM configuration files.
Before go-live, upload, download and delete a test attachment from every EspoCRM node and confirm the IAM policy blocks access to other buckets.
When you run an S3-compatible service such as Ceph RADOS Gateway or another in-house object store inside your data center, the endpoint is a hostname you control, and path-style addressing is often required because the service may not support virtual-hosted bucket names without wildcard DNS. The EspoCRM settings must point at that endpoint explicitly, and the EspoCRM containers or servers must reach it over a path you have engineered and monitored. Your platform team owns the durability of the object store, so the backup plan for it is a separate design from the database backup.
- Endpoint and addressing: Set the custom endpoint to your internal hostname and test whether path-style access is needed for your product version.
- Network path: Place the object store on a segment reachable from the EspoCRM application and any worker containers, and open only the required port.
- TLS trust: Internal certificates must be trusted by the PHP runtime inside every EspoCRM container or host, so plan CA distribution and renewal.
- Backup responsibility: Replicate or back up the buckets to a second failure domain and test restores of individual objects, because the database alone cannot rebuild lost files.
- Capacity and monitoring: Track bucket size and request errors so attachment failures are visible to the operations team.
Confirm with your storage vendor which S3 operations and multipart behavior it supports, then run the upload, download and delete tests from each node.
In air-gapped or tightly segmented networks, there is no route to public AWS endpoints and no external certificate authority. The object store must run entirely inside the enclave, and every component that touches attachments, including the EspoCRM application, workers and any backup tooling, must resolve the internal endpoint and trust the internal certificate chain. Change control and evidence matter more here, because access logs, key custody and restore tests often feed the compliance assessment that your independent auditor performs.
- Endpoint and addressing: Use an internal DNS name for the endpoint, and set path-style access only if your object store requires it after testing.
- Network path: Document every allowed flow between the EspoCRM hosts and the object store, and confirm no traffic leaves the enclave.
- TLS trust: Distribute the internal root and intermediate certificates to PHP and the operating system trust stores, and plan rotation before expiry.
- Backup responsibility: Keep backups inside the enclave under the same classification as the source data, with offline or immutable copies where your policy requires them.
- Credentials and evidence: Store access keys in your secrets system, restrict who can read them, and keep logs of administrative changes.
DeployOpen prepares configuration and evidence for your assessment, but independent auditors issue any audit report, so ask them early which artifacts they will need.
How to configure S3 storage for EspoCRM attachments
This sequence moves EspoCRM attachments from local disk to an S3 or S3-compatible bucket with scoped credentials, tested uploads and a rollback path that keeps local files until the test passes.
Create the bucket and least-privilege policy
The platform or cloud team creates a dedicated bucket for EspoCRM attachments, keeps it private, and writes an IAM or equivalent policy that allows only the object actions EspoCRM needs (put, get, delete, and list on that bucket and prefix). Confirm the exact action list with your storage vendor and with the EspoCRM S3 storage documentation for your version before you attach it. Done means the policy simulator or vendor policy test shows access to the bucket and denies access to other buckets, and public access is blocked.
Provision dedicated access credentials
Create a service identity for the EspoCRM application only, not a personal or administrator account, and generate an access key pair bound to the policy from the previous step. Record the owner, rotation process and where the key is stored in your secrets register. Done means the key is listed under the service identity, and a test call made with only that key can write and read a test object in the bucket but cannot list other buckets.
Pass credentials through environment or secrets
The team who runs the EspoCRM host or container platform injects the access key ID and secret into the EspoCRM process through environment variables or a secrets mechanism such as Docker secrets, so the values never appear in the configuration file under version control. Confirm with the EspoCRM configuration documentation which variables the S3 settings read in your version. Done means the configuration file in the repository contains only placeholder or reference values, and the running process shows the variables are set without printing their contents to logs.
Set the storage options in configuration
The EspoCRM administrator or platform engineer sets the S3 storage options in the EspoCRM configuration, including the bucket name, region, and for non-AWS providers the custom endpoint and path-style addressing if the provider requires it. Keep the local storage settings in place until the test in the final steps passes. Done means the configuration validates on a cache clear or a restart with no errors in the application log, and the new storage values match the bucket and endpoint documented for your provider.
Back up the existing configuration and files
Before any change takes effect, the administrator copies the current configuration file and the local attachment directory to a separate location with a timestamp, and records the file count and total size of the attachments. Use your normal backup tooling or the backup procedure in your EspoCRM operations runbook. Done means the backup restores to a test location and the file count matches the source, so you have a known starting point for rollback.
Upload a test attachment and verify it
An administrator uploads a small test file to a non-sensitive record, then confirms in the bucket console or CLI that the object appears under the expected prefix. Open the attachment in the EspoCRM interface and download it to confirm the content matches. Done means the object exists in the bucket with the correct size, the file opens in the UI, and the application log shows no storage errors.
Migrate existing files and confirm before cleanup
Copy existing local attachments to the bucket using the migration approach your EspoCRM version documents, then spot-check a sample of records across entity types, including large files and older records. Keep the local copies untouched until attachments open correctly for the sample and the object count matches the source. Done means the object count reconciles with the backup, and the test sample opens in the UI. If the test fails at any step, revert the storage settings to local, restore from the backup, and keep the local files as the source of truth until the cause is fixed.
What happens to thumbnails and existing files during migration
Existing attachments stay on local disk until you copy or move them, so switching the storage setting alone does not migrate anything. The database keeps a reference for every attachment, and that reference has to keep matching where the file actually lives. If the setting changes before the files are in the bucket, users will see broken downloads for every older record. Plan the switch as a data move with a defined cut-over point, not as a configuration change. Confirm in the EspoCRM docs and on your own test instance exactly how attachment paths are stored and whether the copy tool you choose preserves them.
A practical sequence is to freeze or schedule uploads, copy the files with checksums recorded for both source and destination, verify that object counts and sizes match, and open a sample of attachments from each entity type through the EspoCRM interface. Only then change the storage setting and run a final incremental copy for anything uploaded during the window. Thumbnails are a separate concern. Cached previews affect how often the application requests objects from the bucket and how quickly list views load, so decide whether they are regenerated, copied or left to rebuild, and measure request volume after the switch rather than assuming it is acceptable.
Keep the local copies for a defined retention window after cut-over so you can roll back if a problem appears. Remove or archive them only after that window closes and a restore test has succeeded. The exact copy tooling, bucket layout and file path structure must be confirmed against the EspoCRM documentation and rehearsed on a test instance with a realistic volume of files before any production attempt. Ask your implementation partner to document the rollback steps in writing as part of the migration plan.
Security controls for attachments stored in object storage
Moving attachments to object storage moves the data out of the EspoCRM host and into a bucket that other systems can reach. The controls below cover who can reach that bucket, how credentials are handled, and how files are protected and recovered. EspoCRM's own security controls still decide who can request a file, so both layers need to be checked together.
Scope the IAM policy to the bucket prefix
Create a dedicated user or role for EspoCRM whose policy allows only the object actions it needs (typically read, write and delete) on the attachment bucket or a prefix inside it. Deny list and admin actions on the account, and keep other applications off this bucket. Ask your team to test the policy against a real upload, download and deletion before go-live, and to confirm that the same credential cannot reach backups or other buckets.
Keep secret keys out of files and images
The access key and secret belong in environment variables or a secrets mechanism supplied at runtime, not in the EspoCRM configuration file, a Compose file committed to Git, or a container image layer. Check that the configuration file on disk does not contain the keys and that the image history does not expose them. Plan a rotation procedure with a named owner, because rotating a key that EspoCRM reads at startup requires a restart.
Block public access on the bucket
Attachments often contain contracts, identity documents and customer correspondence, so the bucket should not allow anonymous reads or public ACLs. Enable the account-level and bucket-level public access block where your provider offers it, and verify it with an unauthenticated request. For S3-compatible platforms, confirm the equivalent setting exists and is enforced, since defaults vary by product.
Encryption at rest and key ownership
Decide whether the provider's default server-side encryption is sufficient or whether your organization needs keys it controls through a key management service. Customer-managed keys add an access dependency: if the key is disabled or deleted, EspoCRM cannot read its attachments. Document who approves key changes and where key policies are reviewed, and confirm the choice with your compliance lead before migration.
Access logging and versioning for recovery
Enable server access logs or the platform's equivalent so that reads, writes and deletions of attachments can be traced to the EspoCRM principal. Turn on object versioning or a retention policy so that accidental overwrites or deletions can be reversed. Confirm that logs are stored in a separate location with restricted access, and that the retention period for logs and versions matches your records policy.
TLS and EspoCRM access controls
Require HTTPS for every request to the storage endpoint, including custom endpoints for S3-compatible services, and reject plain HTTP at the storage layer. Remember that EspoCRM's security guidance, including password policy, two-factor authentication, session limits and IP restrictions, still governs who can open an attachment, because EspoCRM requests the file on the user's behalf. Test both layers together, since a strong bucket policy does not stop a user with a valid EspoCRM login from downloading records they should not see.
Questions teams ask before moving EspoCRM attachments to S3
What does moving EspoCRM attachments to S3 cost, and what drives the price?
The cost is driven by the work involved, not by a standard rate. The main components are design of the bucket and access policy, configuration of the EspoCRM instance, the migration of existing files, testing on a staging copy, and handover documentation. Costs rise with the number and total size of existing attachments, the complexity of the network path to the storage endpoint, the number of environments that must be changed, and how much verification the business requires. Ongoing storage charges come from your storage provider and recur monthly based on volume and requests. Ask for a written quote that separates one-time engagement work from any recurring retainer, and that lists what is excluded, such as storage subscriptions or provider fees.
How long does an S3 migration for EspoCRM take?
Duration depends on what sets the pace: the number and size of files already on local disk, the bandwidth between the EspoCRM host and the storage endpoint, how many environments need to be configured and tested, and how long your change window and approval process take. A small instance with few attachments and a permissive network path moves faster than a large instance with years of files, strict egress rules or several production tenants. Ask for a timeline that breaks out bucket setup, a staging rehearsal, the production copy, the cutover and a rollback check, and confirm which of those steps depend on your team's availability.
Can our internal team move EspoCRM attachments to S3 without a partner?
Yes, if your team has the skills to administer the storage platform, manage IAM credentials, run PHP and the EspoCRM command line, and test restores. The configuration itself is documented in the awsS3Storage settings, and the work is often a small change to the application. The harder parts are usually the file migration, validating that every attachment resolves after cutover, and confirming backup and restore behavior. A partner adds value when internal staff are stretched, when the compliance evidence must be produced in a specific form, or when the instance is business-critical. Decide based on who will own the result after go-live.
What must we provide before the migration starts?
Plan to provide the following before work begins: a bucket or a storage account on the endpoint you approve, with the region or endpoint details; credentials or a role that the EspoCRM host can use, along with a decision on how they will be stored; a staging instance that mirrors production's PHP version, database and file layout; a recent backup of the database and the files directory, with evidence that a restore has been tested; and a change window agreed with the business. You should also name an owner who can approve the cutover and rollback. Confirm these items with the scope document so the schedule does not depend on access that arrives late.
Does storing attachments in S3 change our backup scope?
Yes. Once attachments live in object storage, the database no longer holds the files, so backups must cover both the database and the bucket contents, and they must be restorable together. Confirm whether your storage provider offers versioning, replication or lifecycle rules, and whether those features count as a backup for your policy. Also decide how deleted attachments are handled, because object deletion may be permanent unless versioning is enabled. Keep the local files directory until a full restore test has passed, and document which backup job covers which location.
How do we check that S3 credentials are handled safely?
Keep access keys out of configuration files that are committed to source control. The settings can be supplied from environment variables, and the variables themselves should come from a secrets mechanism your platform already uses. Grant the EspoCRM host an identity limited to the bucket and the actions it needs, and rotate keys on a schedule you can prove. Review who can read the container environment or the server process, because those views can expose secrets. Ask your security team to review the policy document and the logging of object access before cutover.
What should a written quote for this work include?
A useful quote lists the scope in plain terms: which storage endpoint is covered, whether path-style addressing is required for a non-AWS provider, how thumbnail caching is handled, how existing files are migrated and verified, and which environments are included. It should state the deliverables, such as configuration notes and a runbook, the acceptance tests, the change window assumptions, and the responsibilities of your team. It should also name exclusions, including storage provider charges, network changes and any future upgrades of EspoCRM. Ask how changes to scope are priced and approved before work starts.
Limits to confirm in your environment before go-live
Object storage looks interchangeable until EspoCRM writes its first attachment to it. The six checks below cover the version, PHP, API compatibility, addressing, latency, thumbnails and deployment layout that decide whether the setup holds up under real uploads. Confirm each one in the EspoCRM documentation and in a staging test that mirrors production.
Version and PHP extensions
Confirm which EspoCRM release and PHP extensions the S3 integration requires before you plan the change. EspoCRM supports PHP 8.3 to 8.5 on its server configuration page, but the S3 client library and its PHP dependencies must also load on the exact PHP build in your image or server. Check this against the release you run, and plan the test on that same version, since upgrades can change PHP requirements. Record the result in your change request.
S3 API call compatibility
Confirm that your S3-compatible service implements every object operation EspoCRM uses for uploads, downloads, deletes and listing. Vendors differ in how fully they cover the S3 API, in multipart upload behavior and in how they answer requests for missing objects. Ask the vendor for a written compatibility statement, then run an upload, download, delete and a bulk attachment test in staging to confirm the behavior rather than trusting the statement alone.
Path-style addressing behavior
Confirm how the path-style option behaves with your chosen endpoint before you set it. Some non-AWS services need path-style requests because they do not support virtual-hosted bucket names, while AWS and some others expect the bucket in the hostname. A mismatch usually shows up as failed uploads or signature errors rather than a clear configuration message. Test with the exact endpoint value you will use in production, including scheme and port, and keep the setting identical across all application containers.
Latency between app and store
Measure the round-trip time between the EspoCRM application host and the object store, because every attachment view, preview and download makes a request to the bucket. A store in a distant region or behind a slow link can make list views with many attachments feel slow and can stall large uploads. Ask whether your network team can place the store close to the application, and measure response times for typical file sizes in staging before you accept the design.
Existing thumbnails and previews
Confirm whether thumbnails and previews already generated on local disk are copied to the bucket or regenerated after cutover. Copying keeps previews fast but needs a verified file map, while regeneration costs CPU and time on large libraries and can leave some previews blank if source files fail to convert. Ask the team to test a sample of records with images, PDFs and office files, and decide in advance how stale local cache entries will be cleared.
Fit with the deployment layout
Confirm how the storage choice fits your Docker or server layout before you change any file path. A containerized EspoCRM needs its credentials injected from environment or Docker secrets, and any remaining local upload or cache directories must still be writable and included in backups. On a bare-metal Nginx or Apache host, check that PHP-FPM can reach the endpoint through your egress rules and proxy settings. Document which directories still hold data, so the backup plan covers both the database and the bucket.
How DeployOpen scopes and delivers EspoCRM storage changes
DeployOpen treats moving EspoCRM attachments to Amazon S3 or S3-compatible storage as a scoped change to a running system, not a configuration tweak. We confirm your EspoCRM version, hosting layout and storage target first, then build, test and hand over the change inside infrastructure you control. Engagements are fixed-scope, with the scope agreed in writing before work starts.
How we scope
- Confirm your EspoCRM version, PHP runtime and whether uploads currently sit on local disk or a shared volume
- Record the bucket, endpoint, region and addressing style your storage platform requires before any settings are changed
- Agree in writing how existing attachments, thumbnails and backups will be migrated, verified and kept during cutover
- Define who holds the credentials, how they are injected from the environment, and who rotates them
How we build
- Configure the awsS3Storage settings against a non-production instance using a dedicated, least-privilege storage user
- Test uploads, downloads, thumbnail caching and deletions against both AWS S3 and your chosen S3-compatible endpoint
- Run a copy of existing attachments to the bucket, then compare object counts and checksums with the source files
- Review how the change interacts with your backup, restore and upgrade procedures before it reaches production
How we hand over
- Deliver a written runbook covering settings, environment variables, endpoint options and the rollback path to local storage
- Document the migration log, verification results and any files that needed manual handling
- Walk your administrators and platform engineers through restore testing so they can run the procedure without us
- Hand over configuration as code or templates so future changes are reviewed like any other infrastructure change
How we support
- Offer ongoing support, upgrades and continuous testing as a retainer after go-live
- Re-verify storage behavior when EspoCRM, PHP or your storage platform is upgraded
- Keep the storage configuration and runbook current as your hosting layout changes
- Ask us to confirm the scope of any follow-on change in writing before work begins

