Skip to content
Back to Blog
July 28, 2026

Infrastructure as Code for Travel Platforms

How we define, version, and deploy our infrastructure alongside our application code — IaC modules, drift detection, and secret management.

Infrastructure as Code for Travel Platforms
M

"Who changed the production database password and where is it documented?" Nobody knows. The password was changed three weeks ago during an incident response. The engineer who changed it wrote the new password in a direct message to two other engineers and updated the environment variable on the server manually. There's no record of the old password, no record of when the change happened, and no way to verify the change was intentional.

This scenario happens in every team that manages infrastructure manually. Changes happen in response to immediate needs, documentation lags or never happens, and the actual state of the infrastructure drifts from what anyone thinks it is. Infrastructure as code eliminates this category of problem by making the code the documentation.

Infrastructure defined in version control

Illustration for this section

The same principles that make application code manageable apply to infrastructure: version control, code review, automated testing, and automated deployment.

Our infrastructure definition lives in configuration files that describe what the infrastructure should look like. Database instances, server configurations, networking rules, storage buckets, queue definitions, and environment variables are all defined in these files. The files are checked into version control alongside the application code.

When someone needs to change the infrastructure (add a new database, modify a server configuration, update a networking rule), they modify the configuration file and open a pull request. The change is reviewed by another engineer, just like an application code change. The reviewer checks: is this change intentional? Is it safe? Does it affect production? Are there any unintended side effects?

After approval, the change is merged and the deployment pipeline applies it. The pipeline reads the configuration file and makes the actual infrastructure match the definition. If the database instance should have 16GB of memory but currently has 8GB, the pipeline adjusts it. If a new queue should exist but doesn't, the pipeline creates it.

This workflow means every infrastructure change has a commit, a reviewer, a timestamp, and a description. "Who changed the database password" is answered by `git log`. "When did it change" is answered by the commit timestamp. "Why did it change" is answered by the pull request description.

Environment-specific configuration

Our platform runs in three environments: development, staging, and production. The infrastructure is structurally similar across environments (same services, same networking topology, same database schema) but sized differently (development has smaller instances, production has larger ones, staging is somewhere in between).

We handle this with a layered configuration approach. A shared base definition describes the common structure: which services exist, how they connect, what ports they use. Environment-specific overlays adjust the sizing: instance types, replica counts, storage sizes, and scaling thresholds.

The shared base ensures structural consistency. If a new service is added to the base, it appears in all environments. If a networking rule is modified in the base, the modification applies everywhere. Structural drift between environments is prevented by design.

The overlays allow appropriate sizing. Production gets multi-replica database instances with automated backups. Development gets single-instance databases with no backups. The structural definition is the same. The sizing is different.

This layering also applies to environment variables. API keys, database connection strings, and third-party service credentials are different per environment. The configuration references them by name (like `DATABASE_URL`), and the actual values are provided per environment through a secret management system.

Infrastructure change review

Supporting diagram

Infrastructure changes go through code review. This is non-negotiable. A misconfigured networking rule can expose the database to the internet. A botched database resize can cause downtime. An incorrect environment variable can route production traffic to the staging payment processor.

The reviewer checks several things. Does the change only affect the intended environment? A change to production infrastructure should not accidentally modify staging. Does the change require downtime? If so, is a maintenance window scheduled? Does the change affect costs? Adding a larger instance or a new service has a financial impact that should be acknowledged.

We require two reviewers for production infrastructure changes. Not because we don't trust individual engineers, but because production changes have the highest blast radius and the cost of a mistake is highest. Two sets of eyes catch more issues than one.

The review also checks for security implications. Does the change open a new port? Does it modify access controls? Does it change encryption settings? Security-relevant changes get an additional review from an engineer with security expertise.

Drift detection

Drift is when the actual infrastructure doesn't match the code definition. It happens when someone makes a manual change (through a cloud console, a CLI command, or a direct SSH session) without updating the configuration files.

We detect drift by periodically comparing the code definition against the actual infrastructure state. The comparison reports any differences: "The production database has 32GB of memory, but the code says 16GB." "There's a firewall rule allowing port 5432 from 0.0.0.0/0 that isn't in the code." "The staging environment has a queue named 'test-queue' that doesn't exist in the definition."

Drift is treated as a bug. When drift is detected, we investigate: was the manual change intentional (like an emergency fix during an incident) or accidental? If intentional, the code is updated to match the actual state, and the commit documents why the change was made. If accidental, the actual state is reverted to match the code.

The drift check runs daily. Waiting longer means more drift accumulates and the reconciliation becomes harder. Daily checks keep the drift small and the fixes straightforward.

Secret management

Secrets (API keys, database passwords, encryption keys) cannot be stored in version control. A secret committed to a repository is compromised: anyone with access to the repository history can read it, and removing it from the history is difficult.

Our secrets are stored in a dedicated secret management system that provides: encrypted storage (secrets are encrypted at rest), access control (only authorized services and engineers can read specific secrets), audit logging (every secret read is recorded), and rotation support (secrets can be updated without redeploying the application).

The infrastructure configuration references secrets by name, not by value. The database connection string is referenced as `${DATABASE_URL}`, not as the actual connection string. At deployment time, the secret management system injects the actual values into the environment.

For local development, engineers use `.env` files that contain development-specific credentials. These files are git-ignored and never committed. Each engineer manages their own local `.env` file with credentials for development resources.

The rotation workflow is: generate new secret, update the secret management system, deploy the application (which picks up the new secret on restart), verify the application works with the new secret, and optionally revoke the old secret. This workflow is documented and rehearsed. Rotating a secret under pressure during an incident is not the time to figure out the process.

Adopt IaC for your platform

If you're managing infrastructure manually, here's the migration path.

Start by documenting what exists. Before you can define infrastructure as code, you need to know what infrastructure you have. Inventory every server, database, queue, storage bucket, and network rule. This inventory becomes the first version of your infrastructure code.

Define the infrastructure in code. Pick a tool that matches your platform and team's expertise. The specific tool matters less than the practice. What matters is that the infrastructure is defined in a file, version-controlled, and deployed through automation.

Set up drift detection early. The moment you have infrastructure as code, people will be tempted to make manual changes. Drift detection catches these immediately and keeps the code the source of truth.

Move secrets to a management system. This is often the most disruptive change because it requires updating deployment processes and local development workflows. But it's also the most important for security.

Require reviews for all changes. This is a culture change as much as a technical change. Engineers accustomed to making quick manual changes will resist the overhead of a pull request. The first time a reviewed change catches a mistake that would have caused an outage, the resistance fades.


Nowah is an AI travel agent that searches and books real flights and hotels through conversation — no filters, no thirty open tabs. Plan your next trip.

Share this article

Ready to Plan with Nowah?

Bring the idea. Nowah will help turn it into a trip.

Try Nowah