Why secrets management is critical in continuous deployment In any automated deployment pipeline, the application needs to access sensitive resources: databases, external services, image registries, payment systems, or private APIs. These credentials must be injected dynamically at deployment time, but never stored in plaintext in the source code, committed configuration files, or logs visible to developers. The risk is twofold: first, a secret exposed in a commit is exposed throughout the entire Git history, even after deletion; second, a malicious person within the team or someone accessing your CI/CD can compromise your entire production infrastructure. Regulations such as GDPR or PCI-DSS also require demonstrating that no secret is stored in an unencrypted form. In continuous deployment, this injection of secrets must be automatic and transparent for developers, without adding friction to the process: a deployment that slows down or complicates access to secrets encourages the team to bypass best practices. ## The three models for storing secrets in production There are three major approaches to managing secrets in deployment. The first, already outdated but still present in some legacy code, consists of storing the secrets in environment variables on the server or in versioned .env files (without versioning): this works at a very small scale but does not scale, is impossible to audit, and is extremely risky. The second approach uses a centralized, encrypted vault (AWS Secrets Manager, HashiCorp Vault, Google Secret Manager), where secrets are stored in a dedicated, encrypted, auditable service, re-versioned on each change: the application or the pipeline authenticate against the vault at deployment time and retrieve the secret securely. This is the standard in mature organizations. The third approach, often combined with the previous two, uses encryption at the pipeline level: some tools (notably GitOps, which version encrypted secrets directly in Git) store the secrets in encrypted form in the repository, and only the pipeline holds the key to decrypt them at deployment time. Each has advantages and disadvantages depending on your maturity, your budget, and your compliance constraints. For AWS, the combination of a centralized vault (Secrets Manager) and secure injection via the IAM roles of the EC2 or container remains the most efficient and most secure choice. ## AWS architecture: Secrets Manager and secure injection On AWS, the reference pattern combines AWS Secrets Manager for centralized storage and IAM roles for keyless authentication. Here is how it works concretely: you create a secret in Secrets Manager (a database, an API key), define an IAM policy that authorizes only certain resources (your EC2, your Kubernetes pod, your Lambda function) to read it, and at deployment time, the application or the pipeline uses the AWS SDK to retrieve the secret from the established IAM context. There is no static key to manage, no hard-coded secret: trust is based on the identity of the resource that requests the secret. In a CI/CD pipeline (GitLab, GitHub Actions, Jenkins), this means that the pipeline itself is authenticated against AWS via a temporary access key or a role assumption (OIDC is preferred, as it eliminates the need for static keys), and can therefore retrieve the secrets to inject them into the environment variables of the container or the deployed application. Secret rotation is also simplified: Secrets Manager can change the secret automatically according to a defined schedule (for example, weekly rotation of database credentials), and the application never had to memorize the old value, it requests it at each deployment. For even more security, you can combine this with AWS Parameter Store (for non-sensitive configurations) and restrict network access to Secrets Manager via private VPC endpoints. ## Injecting secrets into common deployment pipelines The concrete method of injecting secrets varies depending on your pipeline tool. On GitHub Actions, the practice is simple: GitHub secrets (themselves encrypted and masked from the logs) are injected as environment variables or passed as arguments to the deployment, or you can configure OIDC authentication directly to AWS Secrets Manager to retrieve the secrets on the fly instead of copying them into GitHub. For GitLab CI/CD, the same logic applies via protected variables (limited to the main branches) and CI/CD variables with masking, or a direct integration with an external vault via native integrations. On Jenkins, a plugin (Credentials Binding, HashiCorp Vault plugin) makes it possible to retrieve secrets from a vault and inject them only into the context of the job, without ever writing them to disk. The common golden rule is to always inject secrets as environment variables (never as command-line arguments, visible in the process listing), to mask secrets in the pipeline logs (all major tools offer this function), and to never commit them to the repository, even encrypted locally. In practice, for teams using ECS or Kubernetes with GitHub Actions, the smoothest pattern is: OIDC to AWS STS to obtain a temporary token, then retrieve the secrets from Secrets Manager directly in the deployment (via the IAM role of the task/pod), without ever passing them through the pipeline itself in plaintext. For teams in the migration phase or using multiple clouds, HashiCorp Vault (deployed internally or via HCP) offers a single abstraction layer, cloud-agnostic and very flexible. ## Common pitfalls and how to avoid them The first pitfall, very frequent in rushed teams, is storing secrets in .env files committed by mistake. A single forgotten commit, and you must consider the secret compromised, even if it is deleted later (Git history keeps it). The solution is simple: a strict .gitignore, a pre-commit hook that refuses to commit secret patterns (tools like detect-secrets or TruffleHog help with this), and an audit of the existing code to look for hidden secrets. The second pitfall concerns the logs: applications sometimes write environment variables or vault responses into the logs for debugging, accidentally exposing the secret. Be strict about what you log: never the full environment variables, never the secrets received, never the authentication headers. The third pitfall is insufficient rotation: a secret that you never change becomes a growing risk over time. Use your vault's automatic rotation capabilities (all AWS services, Vault, and modern tools support it). The fourth pitfall is the authentication of the pipelines itself: if you use static access keys (AWS access key / secret key) to authenticate your CI/CD against AWS Secrets Manager, those keys themselves must be managed and stored somewhere. The best approach is OIDC, where the pipeline and AWS exchange short-lived tokens without ever generating static keys. Finally, some teams underestimate the need for auditing: who accessed which secret, when, and why? Secrets Manager and Vault provide built-in access logs; enable them and review them regularly. ## Best practices and a hardening checklist Here are the key points to put in place for secure secrets management in continuous deployment. First, centralize all secrets in a single vault (Secrets Manager, Vault, or equivalent) and remove them from everywhere else: code, config files, server environment variables. Second, authenticate the pipeline without static keys: use OIDC for cloud authentications, or an agent (Vault agent on the server) for on-premise deployments. Third, limit access to the minimum necessary: a payment application does not need to access the reporting database's secrets. Use IAM policies or the vault's access controls to enforce this separation. Fourth, mask secrets in logs and outputs: configure your pipeline and your tools to omit sensitive values from all logs. Fifth, version and audit accesses: enable the vault's audit logs, review them regularly, and trigger alerts on suspicious accesses (an application requesting a secret beyond its normal needs, or a human accessing secrets directly outside of a deployment). Sixth, put in place secret rotation on a strict schedule: monthly or quarterly for database credentials, more frequently for sensitive API tokens. Seventh, test your secrets management procedures thoroughly: a compromised secret must be able to be renewed and deployed within minutes, not hours. Finally, document who has the right to create, modify, or access which secrets, and apply a separation of duties: a developer can create a secret in a dev environment, but only DevOps or an ops person can promote it to production. ## Integrating secrets management into your current AWS pipeline If you already use an AWS infrastructure and a deployment pipeline, the migration to secure secrets management is progressive and without interruption. Start by creating a secret in AWS Secrets Manager for one of your non-critical applications, then update its ECS task definition or its Lambda configuration so that the credentials are injected from Secrets Manager instead of being in plaintext. Test the deployment, verify that the application accesses the secret correctly, then measure the injection time (generally imperceptible). Once the pattern is validated, reproduce it for the other applications. For legacy applications that expect .env files or classic environment variables, the process is identical: the pipeline retrieves the secret and passes it to the application as an environment variable, and the application has nothing to change. For AWS CodeDeploy, the integration is native: configure an IAM policy that authorizes the deployment role to access the secrets, and use a custom call script to retrieve and inject them. For Kubernetes on AWS (EKS), the practice is slightly different: use the AWS Secrets and Configuration Provider (ASCP) to synchronize AWS secrets into Kubernetes secrets, or inject them directly via the pod IAM role (Karpenter, Kube2IAM). The cost is negligible: Secrets Manager bills per secret and per request, generally less than a few euros per month for a small to medium team. The migration can be conducted progressively without risk of regression: if an application cannot access Secrets Manager for any reason, maintain a temporary fallback to the old method, the time needed to debug the IAM access. Once all applications are migrated and tested in production for a few weeks, you can remove the old secrets from the classic infrastructure. ### Concrete steps to start tomorrow Here is what you can do tomorrow morning to put secure secrets management in place. First, inspect your codebase and your current infrastructure to identify all exposed secrets (simple search scripts, tools like TruffleHog, or a quick manual audit). Note the contact details of the owner of each secret (which developer, which project). Second, create a test secret in AWS Secrets Manager: choose a dummy API key, give it a name, and place it in Secrets Manager. Test its retrieval via the AWS SDK or via the CLI. Third, create or update the IAM policy of a non-critical application to give it access to this secret, and verify that the application can retrieve it without error. Fourth, update this application's pipeline (GitHub Actions, GitLab CI, or Jenkins) to go through OIDC to AWS instead of a static key, if it is not already done. Fifth, once this first secret is in production and functioning for a few days, start migrating the other secrets one application at a time, beginning with the least critical. Do not wait to migrate all secrets in a single large operation: that would introduce too much risk. Finally, configure an alert or notification on any access to production secrets (via CloudWatch or your vault's alerting system), and assign someone to review these logs once a week.