Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Multi-Cloud Secrets Management

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.

Multi-Cloud Secrets Management OverviewAWS WorkloadsEC2, ECS, LambdaAzure WorkloadsApp Service, AKSGCP WorkloadsGKE, Cloud RunCentral Secrets LayerVault · AWS SM · Azure KV · GCP Secret ManagerUnified Audit · Rotation · Least Privilege IAMOIDC workload identity — no long-lived keys
Multi-Cloud Secrets Management centralises credentials while workloads on AWS, Azure, and GCP authenticate via short-lived identity.

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_ID in 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.

ApproachBest forTrade-offsTypical cost (small team)
Cloud-native per provider (AWS Secrets Manager, Azure Key Vault, GCP Secret Manager)Workloads stay mostly within one cloud; minimal ops overheadThree APIs, three rotation configs, fragmented auditRs 2,000–8,000/mo (~USD 15–60) across providers
HashiCorp Vault (self-hosted or HCP Vault)True multi-cloud, dynamic DB credentials, PKIOps burden self-hosted; HCP adds subscriptionSelf-hosted: VPS cost; HCP from ~USD 0.03/secret/mo
External Secrets Operator (ESO) + any backendKubernetes on any cloud; GitOps-friendly syncK8s required; adds controller to maintainController 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.

Secrets Backend Decision TreeMulti-cloud workload?No — single cloudCloud-native storeAWS SM / Azure KV / GCP SMYes — 2+ cloudsRunning Kubernetes?No K8sHashiCorp Vault central+ cloud IAM federationK8s yesESO + Vault or cloud KVGitOps-safe syncAll paths: OIDC workload identity, no long-lived access keys
Decision tree for Multi-Cloud Secrets Management — single-cloud teams use native stores; multi-cloud teams centralise via Vault or ESO.

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.

Runtime Secret Injection FlowGit RepoNo secrets committedCI PipelineOIDC → short-lived roleSecrets VaultAWS SM · Azure KV · VaultDeploy / ESO SyncFetch at deploy or refresh intervalLaravel / PHP Runtimeenv vars in memory — never in Git historyAudit LogWho fetched whatWhen · from which IPCloudTrail / KV logsAlert on anomalies
Multi-Cloud Secrets Management injection flow — Git stays clean, CI uses OIDC, vaults supply runtime credentials with full audit.

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:

  1. Store two valid keys in the vault during overlap (STRIPE_SECRET and STRIPE_SECRET_PREVIOUS).
  2. Update the provider dashboard with the new key.
  3. Write the new key to the vault; apps read on next deploy or cache refresh.
  4. After traffic confirms success, revoke the old key and remove the previous slot.
  5. 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.

Zero-Downtime Rotation TimelineTime →Key A activeAll trafficOverlap windowKey A + Key B validKey B activeKey A revoked1. Add Key B to vault · 2. Deploy all clouds · 3. Revoke Key AAWS Secrets ManagerAuto-rotate RDS credsLambda rotation functionAzure Key VaultScheduled key rotationEvent Grid notifications
Multi-Cloud Secrets Management rotation uses a dual-key overlap window before revoking the old credential across all providers.

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 .env creates 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; never echo $DB_PASSWORD in 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

  • GetSecretValue or KeyVaultSecretGet from 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

  1. Enable CloudTrail / Azure diagnostic settings / GCP audit logs on all secret stores.
  2. Require MFA for human vault console access.
  3. Tag secrets with app, environment, and owner for cost and access reviews.
  4. Quarterly access review: list principals with read access; remove stale IAM users and expired CI roles.
  5. Annual rotation drill: simulate key compromise and measure time-to-revoke across all clouds.
Cross-Cloud Audit AggregationAWS CloudTrailGetSecretValue eventsAzure Activity LogKey Vault accessGCP Audit LogsSecret Manager readsCentral Log Store / SIEMOpenSearch · Loki · CloudWatch cross-accountAlert RulesAnomaly · off-hours accessQuarterly ReviewAccess recertification
Aggregate Multi-Cloud Secrets Management audit logs from AWS, Azure, and GCP into one monitoring layer for alerts and compliance reviews.

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.

Frequently Asked Questions

Centralized storage, rotation, and delivery of API keys, database passwords, and tokens across AWS, Azure, GCP, and on-prem servers from one audited system instead of scattered .env files.

When you run Laravel on AWS EC2, a backup worker on DigitalOcean, and GitLab CI deploys to both, copying .env manually creates drift. I've seen production break because staging had an updated Stripe key but the Azure failover server did not. A secrets manager gives one source of truth, audit logs showing who accessed what, automatic rotation, and CI pipelines that pull secrets at deploy time rather than storing them in GitLab variables forever. For legal-tech portals handling payment and document data, that audit trail matters during security reviews.

HashiCorp Vault self-hosted starts around Rs 0 on a small VPS; managed Vault Cloud runs roughly Rs 40,000–80,000/month (~USD 300–600). AWS Secrets Manager costs about Rs 0.53 per secret per month (~USD 0.004).

Adopt it once you have three or more environments, two cloud providers, or any compliance requirement demanding secret rotation and access audit logs.

AWS Secrets Manager and Azure Key Vault are excellent inside their own ecosystems but create vendor lock-in when you also run workloads elsewhere. HashiCorp Vault is cloud-agnostic and the usual choice when AWS hosts production and Azure or a VPS handles backups. Google Secret Manager fits GCP-heavy stacks. For a Laravel shop on AWS EC2 with GitLab CI, I often start with AWS Secrets Manager plus External Secrets or a deploy-time fetch script. Vault earns its complexity once you exceed roughly five services across two or more clouds and need dynamic database credentials.

Keep a minimal .env on each server with only APP_KEY and the secrets manager connection details. At deploy time, use Deployer 7 shared hooks or a GitLab CI job to fetch secrets via the AWS SDK, Azure CLI, or Vault API and write them into the shared .env path outside the release symlink. Laravel reads them normally through config caching. Never commit fetched secrets into the release directory. On projects I've maintained, the Deployer shared directory holds the populated .env while each release stays clean. Run php artisan config:cache after injection so opcache serves the correct values.

GitLab masked and protected variables work for small single-cloud setups, but they are static, hard to rotate across environments, and visible to anyone with Maintainer access. For multi-cloud production, treat GitLab variables as bootstrap credentials only — enough to authenticate to Vault or AWS Secrets Manager during deploy. Fetch application secrets from the manager at runtime. I've seen teams store database passwords directly in GitLab for years; when a developer leaves, there is no clean revocation path. A secrets manager gives per-service policies, TTL-based tokens, and an audit log GitLab variables cannot provide.

Enable automatic rotation in AWS Secrets Manager or configure Vault database secrets engine to generate short-lived credentials. Update the application through a rolling deploy: rotate in the manager, trigger a Deployer deploy or Kubernetes rollout, verify health checks, then revoke the old credential. For Laravel apps using MySQL 8.0, schedule rotation during low-traffic windows and confirm queue workers restart because they cache DB connections. Never rotate payment gateway keys on all clouds simultaneously — update one environment, test webhooks, then proceed. Document the rotation runbook; I've fixed too many midnight outages caused by rotating Stripe keys everywhere at once.

Storing bootstrap tokens for the secrets manager inside the same GitLab repo they protect. Granting every CI job broad read access to all secrets instead of scoped policies per environment. Forgetting to restart PHP-FPM after secret rotation, leaving opcache serving stale config. Running Vault without TLS on internal networks. Copying production secrets into staging for debugging. Using the secrets manager as a general config store for non-sensitive values, inflating cost and complexity. On real client projects, the most common failure is deploying the fetch script but never testing the failover path when the primary cloud region is unavailable.

Yes. Install a Vault PHP client or call the Vault HTTP API from a custom Artisan command or deploy hook. Authenticate via AppRole or JWT from GitLab CI, read KV v2 secrets at path secret/data/myapp/production, and export them as environment variables before config:cache runs. Community packages exist but many teams prefer a thin shell script in Deployer because it keeps secret-fetch logic outside the application codebase. Vault Agent sidecar pattern works on Kubernetes; on Ubuntu with Apache and PHP-FPM, a pre-deploy fetch into the shared .env is simpler and easier to debug.

External Secrets Operator syncs secrets from Vault, AWS, or Azure into Kubernetes Secrets automatically — ideal if your Laravel app runs in EKS or AKS. For traditional VPS or EC2 deployments with Deployer 7 and Apache, ESO adds Kubernetes overhead you do not need. Cloud-native stores like AWS Secrets Manager fit AWS-only stacks cleanly. Multi-cloud Laravel on bare EC2 plus a Hetzner backup server is better served by a deploy-time fetch script or Vault Agent writing to a file. Match the tool to your actual infrastructure; do not adopt Kubernetes patterns on a single-server setup.

Never copy production secrets to staging. Create separate secret paths or namespaces — myapp/staging and myapp/production — in Vault or separate AWS Secrets Manager entries per environment. Use the same key names but different values so deploy scripts stay identical. GitLab CI environment scopes select the correct path via CI_ENVIRONMENT_NAME. For Nepal-based clients on budget hosting, I keep staging on a cheaper VPS with its own test payment gateway keys so a staging mistake never charges a real customer. Document which third-party sandboxes staging uses; developers should never guess.

Existing running instances keep working because secrets are already loaded into PHP-FPM memory and the shared .env on disk. New deploys and horizontal scaling fail if the fetch step cannot reach the manager. Mitigate with cached local copies encrypted at rest, Vault high-availability clusters across availability zones, and health checks that alert before deploy windows. I configure Deployer to abort the release if secret fetch fails rather than deploying with an empty .env — a partial deploy with missing DB credentials is worse than no deploy. Test this by blocking outbound HTTPS to the manager in staging.

Doppler and Infisical offer managed dashboards, team permissions, and CLI sync with minimal ops overhead — good for agencies managing ten to thirty client projects without a dedicated DevOps engineer. Doppler starts around Rs 0 for small teams; paid tiers run Rs 6,500–26,000/month (~USD 50–200). Infisical has a self-hosted open-source option attractive for data-residency concerns. Vault is more powerful but demands ongoing maintenance — TLS, unsealing, upgrades, backup. For a Kathmandu agency deploying Laravel via GitLab CI, I recommend Doppler or Infisical until a client requires on-prem Vault for compliance. Avoid running self-hosted Vault on a Rs 1,500/month shared host.

Store only the secrets manager authentication token in GitLab CI as a masked protected variable scoped to protected branches. In .gitlab-ci.yml, add a before_script step that fetches secrets and writes to shared/.env on the target server via Deployer. Use Deployer 7 shared_files and shared_dirs so .env persists across releases. Restrict IAM or Vault policies so the CI token reads production secrets only from the production branch. Enable audit logging on the manager. Rotate the CI bootstrap token quarterly. After deploy, run php artisan config:cache and reload PHP-FPM. On sister sites I maintain sharing one GitLab pipeline, each site gets its own secret path — never one shared production credential across unrelated client domains.

Share this article

Quick Contact Options
Choose how you want to connect me: