
August 29, 2026
13 min read
Table of Contents
By Kokil Thapa | Last reviewed: August 2026
Your Laravel app runs on AWS EC2, the staging pipeline deploys through Azure DevOps, and a backup job touches Google Cloud Storage — yet every environment still reads the same database password from a committed .env.example or a Slack DM. That pattern breaks the moment you add a second cloud, a second team, or a compliance audit. Multi-Cloud Secrets Management is the discipline of storing credentials in dedicated vaults, fetching them at runtime through short-lived identity, and enforcing one policy layer regardless of where the workload runs. If you already manage production apps across providers, as covered in our multi-cloud architecture guide, secrets are the first subsystem you should centralise — before networking, before DNS, before anything else touches customer data.
What is Multi-Cloud Secrets Management and why does it matter?
Multi-Cloud Secrets Management means one governance model for every credential your systems need, even when compute spans AWS, Azure, Google Cloud, on-prem VPS, or a mix of all four. The secret itself lives in a vault; applications receive it through an authenticated fetch or a sidecar sync, not through environment files checked into version control.
On real client projects — legal-tech portals with document uploads, eCommerce carts with payment gateways, booking systems with SMS APIs — I have seen the same failure mode repeatedly: credentials copied into three places (production server, CI variables, developer laptop), rotated in one, forgotten in two. Multi-cloud amplifies that. Each provider ships its own secrets store, its own IAM model, and its own audit log format. Without a deliberate strategy, you end up with four silos and zero visibility.
The stakes are concrete. A leaked Stripe or Khalti API key can drain a merchant account. A database password in a public Git repo triggers automated bot scans within minutes. Nepal's growing SaaS and fintech scene faces the same threats as global teams; budget constraints often mean one developer wears DevOps, security, and application hats. Centralised secrets management is how a small team keeps parity with enterprise controls without hiring a dedicated security org.
Three principles define a workable multi-cloud secrets posture:
- Never persist secrets in Git. Not in
.env, not in Terraform state without encryption, not in CI logs. Use secrets scanning in Git and CI as a safety net, not a primary control. - Prefer workload identity over static keys. OIDC federation lets GitHub Actions or GitLab CI assume an IAM role without storing
AWS_ACCESS_KEY_IDin pipeline variables. The same pattern works across clouds. - One source of truth per secret class. Either replicate from a central vault into cloud-native stores, or fetch directly from the central vault everywhere. Avoid "sometimes we read from Key Vault, sometimes from a .env on the VPS."
How do you choose a secrets backend for multi-cloud deployments?
You have three realistic patterns in 2026. None is universally correct; the right choice depends on team size, existing cloud spend, and whether Kubernetes is in the picture.
| Approach | Best for | Trade-offs | Typical cost (small team) |
|---|---|---|---|
| Cloud-native per provider (AWS Secrets Manager, Azure Key Vault, GCP Secret Manager) | Workloads stay mostly within one cloud; minimal ops overhead | Three APIs, three rotation configs, fragmented audit | Rs 2,000–8,000/mo (~USD 15–60) across providers |
| HashiCorp Vault (self-hosted or HCP Vault) | True multi-cloud, dynamic DB credentials, PKI | Ops burden self-hosted; HCP adds subscription | Self-hosted: VPS cost; HCP from ~USD 0.03/secret/mo |
| External Secrets Operator (ESO) + any backend | Kubernetes on any cloud; GitOps-friendly sync | K8s required; adds controller to maintain | Controller is open source; backend costs apply |
When cloud-native stores are enough
If your production Laravel app lives on AWS EC2 with RDS, and Azure hosts only a CI runner, keep secrets in AWS Secrets Manager for runtime and Azure Key Vault only for pipeline signing certificates. Replicate cross-cloud secrets manually or through a thin automation script — do not duplicate write paths.
When Vault earns its keep
Vault shines when you need dynamic database credentials (TTL-bound MySQL users created on demand), encryption-as-a-service, or a single audit trail across AWS, Azure, and bare-metal VPS. I've used Vault patterns on production systems where the same API serves clients in Nepal and abroad; dynamic secrets limit blast radius when a contractor leaves or a container is compromised. See our Vault dynamic secrets guide for the database workflow.
When External Secrets Operator bridges GitOps and clouds
On Kubernetes — whether AKS, EKS, or GKE — the External Secrets Operator watches ExternalSecret custom resources and syncs remote vault entries into native Kubernetes Secrets. Your Helm chart references secretKeyRef; the actual value never enters Git. This is the cleanest multi-cloud pattern for containerised PHP/Laravel APIs.
How do you inject secrets into apps without storing them in Git or .env files?
The Twelve-Factor App treats config as environment variables — but that does not mean the values belong in Git. At deploy time, your pipeline or init process populates the environment from a vault. Locally, developers use a secrets CLI or a scoped development vault, never production credentials.
Laravel on AWS EC2 with Secrets Manager
For a Laravel 12 app on Ubuntu with PHP 8.3, fetch secrets during deploy and write a runtime-only .env that never enters Git:
# deploy hook — runs after symlink swap (Deployer, GitLab CI, etc.)
aws secretsmanager get-secret-value \
--secret-id prod/laravel/notary-portal \
--query SecretString --output text > "$RELEASE_PATH/.env"
chmod 600 "$RELEASE_PATH/.env"
chown www-data:www-data "$RELEASE_PATH/.env"
php artisan config:cache
sudo systemctl reload php8.3-fpm Store the secret in AWS Secrets Manager as JSON matching your Laravel keys:
{
"APP_KEY": "base64:…",
"DB_PASSWORD": "…",
"STRIPE_SECRET": "sk_live_…",
"KHALTI_SECRET_KEY": "…"
} The EC2 instance role needs secretsmanager:GetSecretValue on that ARN only — not *. On sister sites I maintain with Deployer 7 and GitLab CI on shared EC2, this pattern replaced hand-edited production .env files and eliminated "works on my deploy" drift.
CI/CD with OIDC — no cloud access keys in GitLab or GitHub
Long-lived AWS_ACCESS_KEY_ID values in CI variables are a common audit finding. Replace them with workload identity federation. GitLab CI can assume an AWS IAM role via OIDC:
# .gitlab-ci.yml excerpt
deploy_production:
id_tokens:
GITLAB_OIDC_TOKEN:
aud: https://gitlab.com
script:
- >
export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s"
$(aws sts assume-role-with-web-identity
--role-arn arn:aws:iam::123456789012:role/gitlab-deploy
--role-session-name gitlab-$CI_PIPELINE_ID
--web-identity-token $GITLAB_OIDC_TOKEN
--query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]'
--output text))
- aws secretsmanager get-secret-value --secret-id prod/laravel/app ...
- dep deploy production The same OIDC trust can be configured for Azure and GCP, giving one identity flow across providers. Our GitHub Actions OIDC deploy guide walks through the AWS trust policy side.
Kubernetes with External Secrets Operator
Define what the app needs declaratively; ESO handles the fetch:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: laravel-app-secrets
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: laravel-env
creationPolicy: Owner
dataFrom:
- extract:
key: prod/laravel/booking-api Your Laravel deployment mounts laravel-env as env vars or as a file. Rotation updates the remote secret; ESO refreshes within the interval; pods restart on the next rollout.
How do you rotate secrets across AWS, Azure, and GCP without downtime?
Rotation is where multi-cloud secrets programs succeed or die. A policy that says "rotate every 90 days" but requires manual updates across three clouds will fail by month two.
Use native rotation where available
AWS Secrets Manager supports automatic rotation for RDS MySQL and PostgreSQL through a Lambda function. Azure Key Vault can rotate storage account keys on a schedule. GCP Secret Manager supports rotation notifications via Pub/Sub. Enable these for database and service-account credentials first — they cause the most damage when stale or leaked.
For application-level API keys (Stripe, SendGrid, payment gateways), native auto-rotation rarely exists. Your pattern:
- Store two valid keys in the vault during overlap (
STRIPE_SECRETandSTRIPE_SECRET_PREVIOUS). - Update the provider dashboard with the new key.
- Write the new key to the vault; apps read on next deploy or cache refresh.
- After traffic confirms success, revoke the old key and remove the previous slot.
- Log the rotation event to your audit system with ticket reference.
Envelope encryption for cross-cloud consistency
When the same secret must exist in AWS and Azure (for example, a shared JWT signing key for an API gateway spanning both), use envelope encryption: a data key encrypts the secret; a KMS key in each cloud wraps the data key. AWS KMS and Azure Key Vault both support this pattern. Our AWS KMS envelope encryption guide explains the mechanics; the multi-cloud twist is replicating the wrapped data key, not the raw secret, across providers.
Zero-downtime Laravel config refresh
After vault rotation, stale Laravel config cache is a silent killer. Always run after secret update:
php artisan config:clear
php artisan config:cache
sudo systemctl reload php8.3-fpm PHP-FPM reload picks up new environment without dropping in-flight requests — the same approach I use after Deployer symlink swaps on production VPS hosts.
What are the common mistakes in Multi-Cloud Secrets Management?
These are not theoretical — I encounter them during production deployments and security reviews on client projects.
- Duplicating secrets instead of referencing them. Copying the same DB password into AWS SM, Azure KV, and a VPS
.envcreates three rotation targets. Pick one writer, many readers. - Over-privileged CI roles. A deploy role with
secretsmanager:*on*lets any pipeline job read every secret. Scope ARNs per environment and per application. - Logging secrets accidentally. Debug output of
env()or Terraform plan can expose values. Mask variables in GitLab CI; neverecho $DB_PASSWORDin scripts. Terraform sensitive outputs must stay marked sensitive. - Ignoring Ansible and IaC secrets. Teams vault application secrets but leave plaintext database passwords in Ansible group_vars. Use Ansible Vault for provisioning secrets and migrate runtime secrets to cloud stores post-bootstrap.
- No break-glass procedure. When Vault is down, someone will SSH in and export credentials manually — unless you document an emergency read path, audit it, and test it quarterly.
- Treating Kubernetes Secrets as secure storage. Base64 is not encryption. ESO or Sealed Secrets encrypt at rest; restrict etcd access; enable encryption providers on the cluster.
Rule of thumb: if removing one developer's laptop from the team would not require rotating credentials, your Multi-Cloud Secrets Management program still has gaps.
How do you audit and monitor secrets access across cloud providers?
Unified audit is hard because CloudTrail, Azure Activity Log, and GCP Audit Logs speak different JSON dialects. Start by forwarding all three to a single SIEM or log bucket — even a self-hosted Loki or OpenSearch instance on a small VPS (Rs 3,000–5,000/mo, ~USD 22–37) beats checking three consoles after an incident.
Events worth alerting on
GetSecretValueorKeyVaultSecretGetfrom an unknown IP or outside business hours- Secret deletion or policy change events
- Failed authentication spikes against Vault or the secrets API
- CI pipeline accessing production secrets from a feature branch
Combine vault audit logs with Gitleaks in CI to catch accidental commits before merge. For Laravel apps handling payment data, map controls to PCI DSS requirement 8 (identify and authenticate access) — our PCI DSS essentials guide covers developer-facing obligations.
Minimum viable audit checklist
- Enable CloudTrail / Azure diagnostic settings / GCP audit logs on all secret stores.
- Require MFA for human vault console access.
- Tag secrets with
app,environment, andownerfor cost and access reviews. - Quarterly access review: list principals with read access; remove stale IAM users and expired CI roles.
- Annual rotation drill: simulate key compromise and measure time-to-revoke across all clouds.
Ship Multi-Cloud Secrets Management on your next deploy
Multi-Cloud Secrets Management is not a vendor purchase — it is an operational contract with your future self. Start small: move production database and payment gateway credentials out of Git into one vault, wire CI through OIDC instead of static keys, enable audit logging, and schedule your first rotation drill. Expand to dynamic credentials and ESO when Kubernetes enters the picture. The teams that get this right treat secrets the same way they treat database backups — non-negotiable infrastructure, not a ticket for someday.
If you are running Laravel, Symfony, or eCommerce workloads across AWS and Azure and want help designing a secrets architecture that survives audits and 3 AM deploys, get in touch — or review our server security guide for the broader hardening checklist that secrets management sits inside.

