Practical Secrets Management for Solo SaaS and Indie Tools
A practical approach for solo founders: how to store, rotate, and scope API keys, database credentials, and third-party tokens without a dedicated security team.
API keys, database passwords, OAuth client secrets, and signing keys all end up in the same place early on: a local .env file and a few environment variables on the host. That works until a laptop is lost, a teammate (or contractor) needs temporary access, a key leaks in a log, or a provider forces a rotation. Solo founders can still keep secrets under control without standing up a full secrets platform on day one.
This guide covers a minimal system that fits early SaaS, AI products, and developer tools. It focuses on what you store, where it lives, who can see it, and how you change it without downtime. The goal is enough structure that a leaked key or a forgotten password does not become a multi-day outage.
Separate secrets from everything else
Treat secrets as a distinct class of data:
- Never commit them to Git, even in private repositories. Add
.env,.env.*,*.pem, andsecrets/to.gitignoreimmediately. - Do not paste them into issue trackers, Slack, email, or support tickets. If a customer or provider asks for a key, generate a short-lived one or use a temporary share.
- Do not log them. Configure your logger and error tracker to redact known secret patterns (authorization headers,
api_key=,password=, Stripe keys, etc.). - Prefer short-lived tokens or scoped keys over long-lived master credentials whenever the provider offers them.
Illustrative example only: a founder keeps the production database URL in the platform’s secret store and a local .env that only contains a read-only replica URL used for debugging. The production URL never touches the laptop after initial setup.
Choose one source of truth per environment
For each environment (local, staging, production) decide where the canonical secret lives and how it reaches the process:
- Local development: a
.envfile that is never committed, or a password manager note that you paste into the shell. Many teams also use a tool that injects secrets into the process environment without writing them to disk. - Staging and production: the platform’s native secret store (environment variables marked secret on Fly, Render, Railway, Vercel, AWS Parameter Store / Secrets Manager, GCP Secret Manager, etc.). The application reads them at startup from the environment; it does not fetch them from a database or a public config endpoint.
- CI: the CI system’s encrypted secret variables, scoped to the repository and the specific jobs that need them. Prefer OIDC or short-lived cloud credentials over long-lived keys when the platform supports it.
Do not keep the same secret in three places “just in case.” One canonical location plus a documented way to bootstrap a new environment reduces the chance that an old key remains valid after you think you rotated it.
Scope and name secrets clearly
Give every secret the least privilege it needs and a name that makes its purpose obvious:
- Database users: one for the application (read/write on the needed schemas), a separate read-only user for analytics or debugging, and no superuser in the application process.
- Third-party API keys: create a key limited to the exact endpoints or resources the product uses. Many providers let you restrict by IP, referrer, or capability.
- Signing and encryption keys: keep them separate from application credentials so a compromise of one does not automatically compromise the other.
- Naming:
STRIPE_SECRET_KEY_PROD,DB_APP_USER_PASSWORD,JWT_SIGNING_KEY. Avoid generic names likeKEYorSECRETthat make rotation and auditing harder.
Store a short description next to the secret in the platform UI or in your password manager: what it is for, when it was created, and the last person who rotated it.
Rotate on a schedule and after any incident
Rotation is the practice that turns a leaked secret from a permanent problem into a temporary one. A practical cadence for solo founders:
- High-value secrets (payment processor, primary database, root cloud credentials): rotate at least every 90 days or immediately after any suspected exposure.
- Lower-value or scoped keys (analytics, email transactional, feature-flag service): every 180 days or when the provider forces it.
- After any incident: any secret that appeared in a log, a support ticket, a public repo, or a compromised laptop should be rotated the same day, even if you are not sure it was abused.
The rotation procedure should be written down so you can follow it under stress:
- Generate the new secret in the provider’s dashboard (or via their API).
- Add the new value to the secret store alongside the old one if the application can accept either (dual-running), or update the store and restart if not.
- Deploy or restart the application so it picks up the new value.
- Verify the new secret works (a simple health check or a test call).
- Revoke or delete the old secret in the provider.
- Confirm nothing is still using the old value (check error logs and any secondary systems).
Illustrative example only: for a Stripe secret key the founder creates a new restricted key, updates the platform environment variable, restarts the app, confirms a test payment still succeeds, then revokes the old key in the Stripe dashboard. The whole process takes a few minutes because the steps are already written down.
Limit who and what can see a secret
Even as a solo founder you will eventually grant temporary access:
- Prefer platform features that let you grant a person or a CI job access to a specific secret without showing the value in the UI.
- When you must share a value, use a one-time secret link (many password managers and tools offer this) that expires after a short window and can be viewed only once.
- Remove access as soon as the task is done. Do not leave contractor accounts or old CI tokens active “in case we need them again.”
- Audit the list of people and systems that can read production secrets at least once a quarter. Delete anything you no longer recognize.
Practical checklist before you add the next secret
- The secret is not committed to Git and is covered by
.gitignore. - It lives in one canonical store for that environment (local
.envor platform secret store). - The application reads it from the environment at startup; it is not fetched from a public endpoint or stored in the database.
- It has the least privilege possible (scoped key, limited DB user, restricted API permissions).
- It has a clear name and a short note about its purpose and creation date.
- Logging and error tracking are configured to redact it.
- You know how to rotate it without downtime and have tested the procedure once.
- Access to the secret store is limited to the people and systems that actually need it.
- A backup or recovery path exists for the secret store itself (platform account recovery, password-manager export stored offline).
Common pitfalls
- Keeping the production database password in the same
.envfile that is copied to every developer laptop. - Logging the full environment or the full request headers on error, which captures secrets.
- Creating a new key for a contractor and never revoking it after the work ends.
- Rotating a secret in the platform UI but forgetting to restart the application, so old processes keep using the previous value until they crash.
- Using the same long-lived key across local, staging, and production, so a local leak becomes a production incident.
- Storing secrets in a shared Google Doc or Notion page “temporarily” and then forgetting about it.
When the simple pattern is no longer enough
Revisit the design when:
- You have multiple people who regularly need production access and the platform’s sharing controls are no longer sufficient.
- You need automatic rotation, short-lived credentials, or fine-grained audit logs that your current host does not provide.
- Compliance requirements demand a dedicated secrets manager with hardware-backed keys or detailed access history.
- The number of secrets grows large enough that naming, ownership, and rotation tracking become error-prone in a flat environment-variable list.
Until then, one canonical store per environment, least-privilege keys, a written rotation procedure, and regular access reviews will prevent the majority of secret-related incidents that solo founders encounter. The system should stay small enough that you actually maintain it.
Secrets are only as safe as the process that creates, uses, and retires them. A few clear rules and a checklist you can follow under pressure are more valuable than a sophisticated tool you never configure.
Sponsor this article · $29 / 30 days
More in Developer Tools
Practical Configuration and Credential Handling for Indie Developer Tools
Practical configuration and credential-handling patterns for solo maintainers of CLIs, SDKs, and small developer tools.
Practical Opt-In Telemetry for Indie Developer Tools
A lightweight, privacy-conscious approach for solo founders who maintain CLIs, SDKs, or small libraries: what to meas...