Understanding Platform.sh Architecture
Immutable Environments and GitOps Model
Platform.sh operates on the principle of immutable environments—each environment is created from a Git branch and inherits infrastructure configuration from the .platform
and .platform.app.yaml
files.
- Every code change triggers a full build and deploy lifecycle
- Service definitions (e.g., databases, caches) are version-locked per environment
- Infrastructure-as-code governs service orchestration
Build and Deploy Separation
The platform separates the build (compile-time) from deploy (runtime) phases. Misunderstanding this separation often causes:
- Runtime failures due to missing files (e.g., built assets not committed)
- Confusion over environment variables available during build vs. deploy
Common Enterprise-Level Issues
1. Environment Drift Across Branches
- Configurations diverge subtly between staging and production
- Different versions of services (e.g., MariaDB 10.3 vs. 10.5) break migrations
- YAML configs copied manually instead of merged through Git
2. Service Dependency Failures
- Applications fail to connect to services due to late starts or misconfigured routes
- Custom services missing readiness probes
3. Build Failures in Monorepo Structures
- Incorrect
mount
orsource
directives in monorepos - Shared dependencies not vendored correctly
Diagnostics and Observability Tools
Activity Logs and Deploy Reports
Each environment has detailed activity logs accessible via CLI or web UI:
platform activity:logs -e staging
Inspect Build Logs Separately
Use the following to isolate build-phase issues:
platform build:log -e feature/login-refactor
Environment Variables and Relationships
Dump and inspect environment variables for service bindings:
printenv | grep PLATFORM_RELATIONSHIPS | base64 --decode | jq
Architectural Pitfalls
1. Configuration Duplication
Teams often duplicate YAML configs across branches. This leads to divergence and subtle bugs. Use shared includes or Git merges to enforce consistency.
2. Overloaded Services
Many teams underestimate required resources, leading to OOM errors or slow response times under load. Define memory and disk explicitly in .platform/services.yaml
.
3. Build-Time vs Runtime Confusion
Placing runtime-only logic (e.g., API keys, secrets) in the build hook leads to inaccessible configuration during actual deployment.
Step-by-Step Troubleshooting Guide
1. Verify Service Version and Health
Inspect service containers:
platform ssh -e prod -- php -r 'print_r(getenv());'
Confirm correct versions of services (e.g., PostgreSQL):
platform relationships -e prod
2. Inspect File Persistence and Mounts
Ensure writable directories are defined correctly:
mounts: "/data": "shared:data"
3. Debug Routing and Endpoint Mismatches
Check .platform/routes.yaml
for proper upstreams and routes:
https://{default}/: type: upstream upstream: "app:http"
4. Isolate Failing Services
Spin up a new environment with minimal dependencies to test isolated failures. Use platform environment:branch
to replicate production.
5. Use the Debug Container
Launch a sandboxed container for deep inspection:
platform ssh -e staging --container debug
Best Practices for Scalable Use of Platform.sh
- Keep configuration centralized and version-controlled via Git
- Define clear build and deploy hooks in
.platform.app.yaml
- Use environment variables and
relationships
to decouple services - Implement CI jobs that validate configuration before merge to main
- Automate promotion pipelines using
platform environment:merge
Conclusion
While Platform.sh simplifies much of DevOps, its Git-based, immutable environment model requires precise configuration and disciplined development practices. Most production issues stem not from Platform.sh itself, but from subtle misuse of its configuration paradigms. By understanding build/deploy separation, centralizing YAML configs, and using the CLI for observability, teams can maintain resilient, scalable environments with minimal friction.
FAQs
1. Why does my code build locally but fail on Platform.sh?
Platform.sh uses a clean build environment. If your build relies on local files not committed to Git or runtime secrets, it will fail during the build phase.
2. How can I test service connectivity before full deployment?
Use a debug container or spin up a staging environment with minimal services to validate connectivity via CLI tools like curl or netcat.
3. What causes "Permission denied" errors in write directories?
By default, most directories are read-only. You must explicitly define writable mounts in .platform.app.yaml
.
4. How do I prevent environment drift?
Always merge configuration changes through Git rather than manual editing. Use pull requests to propagate changes downstream.
5. Is it possible to cache dependencies between builds?
Yes. Use the cache
directive in .platform.app.yaml
to persist directories like node_modules
or vendor
across builds for faster performance.