Case Study: Rebuilding SuiteCRM for a European Textile Company
An anonymized migration story from a European textile company whose CRM needed to move from a fragile container setup to a reproducible, version-controlled SuiteCRM deployment with Google sign-in, email, calendar sync, and upgrade-safe customization.
The first request sounded straightforward: get Google login, email, and calendar integration working inside SuiteCRM. The real problem was deeper. The CRM was running from a pre-built container, managed manually, and carrying business-critical customizations that could disappear during a rebuild.
The client was a European textile and apparel company with a sales process spread across multiple markets. Their CRM was not a side tool. It held customer records, scheduling workflows, and day-to-day team coordination. That meant the fix could not be a quick patch on top of an already brittle foundation.
I rebuilt the deployment around SuiteCRM 8.10.1, Docker Compose, a custom PHP/Apache image, version-controlled configuration, Google OAuth, automated calendar provisioning, and upgrade-safe SuiteCRM extensions. The outcome was a CRM that could be reproduced, debugged, upgraded, and handed over without relying on memory or container state.
The CRM was working, but it was not maintainable
The original deployment used a pre-built SuiteCRM image. That made the first installation fast, but it created a black box once the system needed real production customization. Application code, runtime files, uploads, cache, sessions, and custom PHP changes were all mixed together.
Key deployment details such as environment variables, mounts, ports, and networking existed only as runtime state. Recreating the container meant guessing how production had been assembled.
The goal was to make those decisions explicit in code so the CRM could be rebuilt, reviewed, and upgraded without depending on memory.
A clean Docker architecture replaced the black box
I moved the project to a custom Docker setup based on a standard PHP 8.2 Apache image. That gave us control over PHP extensions, Apache modules, Composer, Xdebug, cron, filesystem permissions, and the actual SuiteCRM source layout.
The project gained a Dockerfile, Docker Compose configuration, Apache and PHP config, a startup entrypoint, and a Makefile for common operations. Instead of asking the client to remember long Docker commands, the system exposed practical commands for building, restarting, viewing logs, opening a shell, clearing cache, accessing the database, and fixing permissions.
The most important infrastructure detail was the entrypoint. It recreates required runtime directories and resets ownership for cache, temp, logs, and uploads whenever the container starts. That prevents a common PHP Docker failure mode where sessions, CSRF tokens, cache rebuilds, or uploads break because a host user or root-owned process touched files.
8.10.1
SuiteCRM upgrade
Migrated from an older pre-built container to a clean SuiteCRM source deployment.
1 command
repeatable operations
Common tasks were wrapped in Makefile commands for build, logs, shell, cache, and permissions.
6
languages supported
CRM field labels and dropdown values were localized for the client team.
0
manual calendar setup
Calendar accounts are provisioned automatically after Google sign-in.
Google SSO had to work across SuiteCRM's two worlds
SuiteCRM 8 has a modern Symfony backend and a legacy CRM layer. Authentication must satisfy both. The old setup relied on several separate files and redirects, which made the flow difficult to reason about and fragile under change.
I consolidated the Google sign-in process into a Symfony controller. The controller handles the Google callback, exchanges the authorization code, validates the user's email domain rules, finds or creates the CRM user, creates the Symfony session, creates the legacy session, stores OAuth tokens, and sends the user to the CRM dashboard.
Because the SuiteCRM frontend was precompiled, I avoided rebuilding the Angular app. A small JavaScript enhancement injects the Google sign-in button into the login screen after the Angular form renders. It is a pragmatic solution: minimal surface area, no frontend build pipeline, and clear behavior for users.
The reverse proxy also needed attention. The CRM was behind TLS termination, while the container saw plain HTTP. Without explicit trusted proxy configuration, Symfony generated HTTP callback URLs and Google rejected them. Configuring trusted proxies and forwarded headers made OAuth redirect URLs match the public HTTPS address.
Calendar sync became automatic after sign-in
The previous calendar implementation used legacy Google sync behavior and required more manual setup than the client wanted. SuiteCRM 8.10.1 includes a newer framework around external OAuth providers, OAuth connections, calendar accounts, and scheduler jobs.
The problem was discoverability. Calendar account setup is not obvious for a non-technical CRM user, and asking every employee to configure it correctly would create support debt.
I added automatic provisioning during Google sign-in. When a user authenticates, the CRM checks whether a Google calendar account already exists for that user. If not, it links the user to the configured Google OAuth connection and creates the calendar account needed by SuiteCRM's sync job. From the user's perspective, signing in once is enough.
Custom CRM fields were built the upgrade-safe way
The company needed CRM fields for its sales workflow: country coverage, pricing tier, decoration method, and customer type. Those options also needed translations for six languages used by the team.
I implemented the fields through SuiteCRM's Extension framework instead of modifying core files. Dropdown values, field definitions, and labels live under the custom extension paths, then SuiteCRM compiles them during repair and rebuild.
This matters because CRM customization is a long-term maintenance problem. A core edit may work today and fail during the next upgrade. Extension-based customization keeps the implementation visible, version-controlled, and much easier to carry forward.
Before and after
| Area | Before | After |
|---|---|---|
| Infrastructure | Pre-built image with runtime-only configuration | Custom Dockerfile, Compose, and Makefile |
| Version control | No reliable source history for customizations | Git-backed source, config, and custom CRM code |
| Google SSO | Fragile legacy flow across multiple layers | Single Symfony flow with user provisioning |
| Calendar sync | Legacy setup with manual user friction | Native sync framework with auto-provisioning |
| Permissions | Random cache, session, and ownership failures | Self-healing startup and clear ownership model |
| Custom fields | Hard to maintain across upgrades | SuiteCRM Extension framework with translations |
The final system is not just a CRM with Google login. It is a maintainable application deployment. The client can rebuild it, inspect it, debug it, update it, and continue extending it without treating the production container as the only source of truth.
Lessons from the migration
- Pre-built images are useful for demos, not long-term customization. Once a CRM becomes business-critical, the deployment needs source control and reproducible infrastructure.
- Docker permissions deserve first-class design. PHP apps write sessions, cache, uploads, and logs constantly. Ownership mistakes become user-facing bugs quickly.
- SuiteCRM 8 authentication crosses multiple layers. Symfony sessions, legacy sessions, OAuth tokens, and frontend entry points all need to line up.
- Reverse proxies affect OAuth. If the application does not trust forwarded HTTPS headers, redirect URLs can silently become wrong.
- Automation beats documentation for repeated setup. Auto-provisioning calendar sync removed a support process before it became one.
- Upgrade-safe customization is non-negotiable. SuiteCRM's Extension framework is the right place for field definitions, labels, and dropdowns.
Quick answers
Why not keep using the pre-built SuiteCRM image?
The pre-built image was fine for a quick start, but it hid too much once the system needed custom Google OAuth, calendar sync, CRM fields, debugging, and reliable upgrades. A custom Docker setup gave the client control and repeatability.
What made this migration difficult?
The hard parts were not only installing SuiteCRM. The real complexity was preserving Google integrations, making authentication work across Symfony and legacy SuiteCRM, handling proxy-aware OAuth URLs, keeping Docker volume permissions stable, and making custom fields survive upgrades.
Work with me
Need a CRM migration that can survive production?
I build and rescue full-stack systems with PHP, Vue, Nuxt, Docker, automation, OAuth, and business workflow integrations. If your CRM or internal platform is too important to keep patching blindly, I can help turn it into a maintainable product.