DEV Community

Cover image for Solved: What’s a “don’t do this” lesson that took you years to learn?
Darian Vance
Darian Vance

Posted on • Originally published at wp.me

Solved: What’s a “don’t do this” lesson that took you years to learn?

🚀 Executive Summary

TL;DR: Manual server changes lead to catastrophic configuration drift, causing system instability and outages. The solution involves adopting robust DevOps practices like Infrastructure as Code, Configuration Management, and GitOps to enforce consistency and automate infrastructure management.

🎯 Key Takeaways

  • Stop making manual, ad-hoc changes directly on servers to prevent configuration drift, which causes mysterious failures and outages.
  • Implement Infrastructure as Code (IaC) with tools like Terraform to define and manage infrastructure declaratively, ensuring version-controlled, repeatable, and auditable provisioning.
  • Utilize Configuration Management tools such as Ansible to enforce the desired software and OS state within servers, ensuring idempotency and automatic healing of systems.
  • Adopt GitOps, especially for Kubernetes environments, to make Git the single source of truth for the entire system, automating deployments and state synchronization via continuous agents like Argo CD.

Discover why manual server changes lead to catastrophic configuration drift and system instability. This post details three robust DevOps solutions—Infrastructure as Code, Configuration Management, and GitOps—to enforce consistency and prevent production disasters.

The Symptoms of a ‘Quick Fix’ Culture

You’ve been there. It’s 2 AM, production is down, and the pressure is on. You find the problem: a misconfigured Nginx setting on one of the web servers. The fastest way back to green is to SSH into the box, edit the nginx.conf file with vim, and restart the service. The site comes back up. You’re the hero. You close the ticket, promising yourself you’ll “document it tomorrow.”

But tomorrow never comes. Six months later, you’re trying to scale up, and the new server you provisioned from the “golden image” doesn’t work. It takes half a day to discover that the new server is missing the critical fix you applied manually that night. This is the insidious disease of configuration drift, and it’s a lesson that often takes years of painful outages to truly learn. The “don’t do this” is simple, yet profound: stop making manual, ad-hoc changes directly on your servers.

Common symptoms that your team is suffering from this problem include:

  • Deployments that fail mysteriously on a subset of servers.
  • Outages caused by routine maintenance or scaling events.
  • Engineers spending hours comparing config files line-by-line between “working” and “broken” servers.
  • A general fear of touching certain “fragile” parts of the infrastructure.
  • The dreaded phrase, “But it works on the staging server!”

These are not isolated incidents; they are systemic failures caused by treating servers like pets instead of cattle. It’s time to change the approach.

Solution 1: Establish a Foundation with Infrastructure as Code (IaC)

The first step away from manual changes is to stop creating infrastructure with a GUI or CLI. Infrastructure as Code (IaC) means defining and managing your infrastructure—servers, load balancers, databases, networks—using definition files, not manual processes.

How It Works

Tools like Terraform, AWS CloudFormation, or Azure Resource Manager (ARM) allow you to declare the desired state of your infrastructure in code. This code is version controlled in Git, reviewed by peers, and applied automatically.

Instead of clicking through the AWS console to create a new EC2 instance, you define it in a Terraform file. Need to change the instance type? You don’t SSH in; you update the code, run terraform apply, and let the tool handle the change safely.

Example: Provisioning an EC2 Instance with Terraform

Here’s a simple example of defining a web server. This file is the single source of truth for this resource.

provider "aws" {
  region = "us-east-1"
}

resource "aws_instance" "web_server" {
  ami           = "ami-0c55b159cbfafe1f0" # Ubuntu 20.04 LTS
  instance_type = "t2.micro"

  tags = {
    Name = "WebServer-Prod-01"
  }
}
Enter fullscreen mode Exit fullscreen mode

To upgrade this server to a t3.small, you change one line of code (instance_type = "t3.small"), commit it to Git, and apply the change. The process is documented, repeatable, and auditable.

Solution 2: Enforce State with Configuration Management

IaC is fantastic for provisioning the “box,” but what about the software and configuration *inside* the box? That’s where configuration management tools like Ansible, Puppet, Chef, or SaltStack come in. Their job is to ensure a server continuously conforms to a defined state.

How It Works

These tools replace manual SSH sessions and shell scripts. You define the desired state—packages to be installed, files to be created, services to be running—in a structured format. The tool then connects to your servers and enforces that state. A key concept here is idempotency: running the configuration multiple times produces the same result without causing errors, ensuring a consistent end state.

Example: Installing and Configuring Nginx with Ansible

This Ansible playbook ensures Nginx is installed and running with a specific configuration file.

---
- name: Configure Web Server
  hosts: webservers
  become: yes
  tasks:
    - name: Install Nginx
      apt:
        name: nginx
        state: present
        update_cache: yes

    - name: Copy Nginx configuration
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify:
        - Restart Nginx

    - name: Ensure Nginx is running and enabled on boot
      service:
        name: nginx
        state: started
        enabled: yes

  handlers:
    - name: Restart Nginx
      service:
        name: nginx
        state: restarted
Enter fullscreen mode Exit fullscreen mode

You run this playbook against your fleet. If someone logs in and manually stops Nginx, the next Ansible run will detect the drift and restart the service, automatically healing the system.

Solution 3: Automate the Workflow with GitOps

GitOps is the pinnacle of this philosophy, primarily used in the world of containers and Kubernetes. It makes Git the undeniable single source of truth for the entire system. Any change to production—be it an application update or an infrastructure change—happens through a Git commit and pull request.

How It Works

In a GitOps model, an automated agent (like Argo CD or Flux) is installed in your Kubernetes cluster. This agent continuously compares the live state of the cluster with the desired state defined in a Git repository. If there’s a discrepancy, the agent automatically syncs the cluster to match the repository. Direct access to the cluster (e.g., via kubectl apply) is heavily restricted or eliminated entirely for engineers.

Example: A GitOps Workflow

  1. A developer needs to update the container image for the frontend service from version v1.1 to v1.2.
  2. They create a new branch in the configuration repository.
  3. They change the image tag in the Kubernetes deployment YAML file:
   # before
   spec:
     template:
       spec:
         containers:
         - name: frontend
           image: my-company/frontend:v1.1

   # after
   spec:
     template:
       spec:
         containers:
         - name: frontend
           image: my-company/frontend:v1.2
Enter fullscreen mode Exit fullscreen mode
  1. They open a pull request. The PR is reviewed, CI tests pass, and it’s merged to the main branch.
  2. The GitOps agent (Argo CD) detects the change in the main branch.
  3. Argo CD automatically applies the change to the Kubernetes cluster, triggering a rolling update of the frontend service.

The entire process is automated, auditable via Git history, and inherently safe. Rolling back is as simple as reverting a Git commit.

Comparing the Approaches

These three solutions are not mutually exclusive; they are layers of a mature, automated system. IaC builds the house, Configuration Management furnishes it, and GitOps is the automated estate manager that ensures everything stays in its perfect place.

Aspect Infrastructure as Code (IaC) Configuration Management GitOps
Primary Focus Provisioning and managing core infrastructure resources (VMs, networks, databases). Configuring the software and OS state *within* the provisioned infrastructure. Managing the entire application and infrastructure state declaratively via a continuous sync process.
Typical Tools Terraform, CloudFormation, Pulumi Ansible, Puppet, Chef, SaltStack Argo CD, Flux, Jenkins X
Mode of Operation Declarative. You define the “what,” and the tool figures out the “how.” Usually applied manually or in a CI/CD pipeline. Can be declarative (Puppet) or procedural (Ansible). Often runs on a schedule or is triggered to enforce state. Declarative and continuous. An in-cluster operator actively pulls the desired state from Git.
Best Use Case Building your foundational cloud environment from scratch. Disaster recovery. Managing fleets of virtual machines or bare-metal servers, ensuring they are identical. Managing Kubernetes-native applications and infrastructure where the entire system state can be described in YAML/code.

The Lesson Learned: Embrace Auditable, Automated Change

The hard lesson that takes years to sink in is that the speed gained from a “quick manual fix” is an illusion. It’s a high-interest technical debt that you will inevitably pay back with hours or days of troubleshooting, instability, and outages. The real path to velocity and stability is through discipline.

By treating your infrastructure and configuration as code, you transform your operations from a reactive, stressful art form into a predictable, scalable engineering practice. Every change is documented, reviewed, tested, and automated. This is the foundation of a resilient system and, more importantly, a sane work-life balance for you and your team.


Darian Vance

👉 Read the original article on TechResolve.blog


Support my work

If this article helped you, you can buy me a coffee:

👉 https://buymeacoffee.com/darianvance

Top comments (0)