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.

Immutable vs Mutable Infrastructure

By Kokil Thapa | Last reviewed: September 2026

Immutable vs mutable infrastructure is the choice between replacing whole servers or containers from a known build, and logging into live boxes to patch packages by hand. That split sounds academic until a production Laravel app behaves differently on staging and live because someone ran apt upgrade on one server three months ago. On real client projects I have maintained since 2010, the teams that win treat servers as cattle in immutable patterns, or they document every mutable change and accept drift as a cost. This guide compares both models with commands, trade-offs, and patterns you can apply on Ubuntu, EC2, and typical PHP-FPM stacks today.

The practical starting point for many PHP shops is not Kubernetes. It is a single EC2 instance, Apache or Nginx, PHP-FPM 8.3 or 8.4, and a Git-based deploy. Whether that deploy is immutable infrastructure built from golden images or a mutable symlink swap depends on team size, release frequency, and how much drift you can tolerate. Read on for a side-by-side comparison, then a decision path you can act on this week.

What is the difference between immutable and mutable infrastructure?

Mutable infrastructure treats each server as a long-lived pet. You SSH in, install packages, edit config files, and restart services. The machine accumulates history. Immutable infrastructure treats each deploy unit as disposable. You build once, promote the artifact, and replace the running instance rather than patching it.

Both models can coexist. A mutable base OS image might boot from cloud-init, then an immutable application layer ships as a container or tarball. The label applies to the layer you are changing, not the entire stack.

Immutable vs Mutable InfrastructureMutable (Pets)SSH, apt, live editsState lives on the boxDrift over timeImmutable (Cattle)Build image, replaceVersioned artifactsRollback = old imageShared goal: reproducible productionIaC + CI/CD + monitoringSee: golden images, GitOps, Deployer releases
Immutable vs mutable infrastructure: mutable servers change in place; immutable units are replaced from versioned builds.

Infrastructure as Code (IaC) helps both sides. Terraform or Bicep can declare desired cloud resources. Chef, Ansible, or shell scripts can enforce mutable state. Packer and container builds produce immutable artifacts. The mistake is assuming IaC alone makes you immutable. Running ansible-playbook against the same server for three years is still mutable unless you rebuild the host from scratch on every change.

Core terms you will hear in production

  • Golden image — a tested AMI, VM template, or container base built by Packer or a CI pipeline.
  • Configuration drift — when live servers diverge from documented intent.
  • Phoenix server — destroy and recreate instead of repair.
  • Blue/green or rolling replace — traffic moves to new instances while old ones retire.

How does mutable infrastructure cause drift in production?

Drift is the silent tax on mutable infrastructure. Server A and Server B both run Laravel 12 on PHP 8.3. One had OpenSSL patched manually after a CVE alert. The other did not. A queue worker on the second box now fails TLS to a payment API. You spend an afternoon diffing packages instead of shipping features.

I have seen this on shared EC2 hosts that run multiple legal-tech portals under one Apache vhost layout. A quick pecl install on one site’s troubleshooting session changed the global PHP module load order. Another site broke until we traced the shared php.ini chain.

Mutable Drift Over TimeDay 1Identical serversMonth 3Manual patchMonth 6Hotfix on prodDriftSymptoms teams noticeStaging passes, production fails on same commitCannot reproduce bug on a fresh VMRollback restores code, not OS packagesFix: rebuild from IaC or golden image
Mutable infrastructure drift: manual changes on live servers create environments that no longer match staging or documentation.

Mutable workflows still dominate small teams because they feel fast. SSH plus apt install redis-server takes minutes. Standing up Packer, an AMI pipeline, and autoscaling groups takes days or weeks. For a single-server Laravel shop billing Rs 15,000/month (~USD 112) on hosting, full immutability at the VM layer is often overkill. Application-level immutability through release directories is enough.

Signs your mutable servers need a reset

  1. dpkg -l output differs between staging and production for the same role.
  2. Nobody can list every manual change from the last year.
  3. Disaster recovery means “find the backup” rather than “run Terraform apply.”
  4. Security patches land on some nodes but not others during busy weeks.

When drift crosses that threshold, Linux system administration work shifts from firefighting to reprovisioning. That is the inflection point where immutable patterns pay back the upfront cost.

How does immutable infrastructure work in practice?

Immutable infrastructure follows a build-release-run loop described in the Twelve-Factor App build-release-run model. You separate the build stage (compile assets, run tests, bake an image) from the release stage (tag artifact v2026.09.11) from the run stage (launch instances from that tag only).

On AWS, a common pattern is: Packer builds an AMI with PHP 8.5, Nginx, and hardening applied. Terraform launches an Auto Scaling Group from that AMI. User data or cloud-init pulls the latest application release artifact from S3 or a container registry. When PHP minor versions change, you bump the Packer template, build ami-NEW, update the launch template, and roll instances. You do not apt-upgrade running boxes.

Immutable Deploy PipelineGit pushCI buildGolden AMINew instancesReplace, do not patchOld ASG instances terminated after health checks passRollback = previous AMI or container tagData stays on RDS, Redis, or EFS — not on the VMHashiCorp Packer + Terraform pattern
Immutable infrastructure pipeline: CI builds a versioned artifact, new instances launch from it, and old instances retire after health checks pass.

The HashiCorp Packer documentation describes image builders for AWS, Azure, GCP, and Docker. A minimal Packer template for Ubuntu 24.04 might install PHP-FPM and harden SSH. That image becomes the only approved way to spawn app servers.

Containers push immutability further. A Docker image digest is immutable by design. Kubernetes replaces pods rather than exec-ing package installs inside them. For Laravel 13 on PHP 8.3+, a Dockerfile that runs composer install --no-dev and bakes config cache into the image gives you predictable runtime. Stateful data belongs in MySQL 9.7 or PostgreSQL 18, Redis 8.10, or object storage — never inside the container filesystem you treat as ephemeral.

Laravel on a single VM: immutable releases without replacing the OS

Not every project needs new AMIs per deploy. Deployer 7 on a persistent EC2 box is a hybrid that works well for sites like Adventure Third Pole Trek and sister legal-tech portals I maintain on shared infrastructure. The OS layer stays mutable. The application layer is immutable per release.

# deploy.php (Deployer 7)
set('repository', 'git@gitlab.com:org/booking-app.git');
set('shared_dirs', ['storage']);
set('shared_files', ['.env']);
set('writable_dirs', ['storage', 'bootstrap/cache']);

task('deploy', [
    'deploy:prepare',
    'deploy:vendors',
    'artisan:storage:link',
    'artisan:config:cache',
    'artisan:route:cache',
    'artisan:view:cache',
    'deploy:publish',
    'php-fpm:reload',
]);

Each deploy creates releases/202609111430 with a fresh vendor/ tree. The current symlink swaps atomically. Rollback is dep rollback — point the symlink at the previous release folder. That is immutable application deployment on a mutable host. It satisfies idempotent infrastructure principles at the code layer even when the kernel packages were upgraded manually last month.

GitLab CI builds frontend assets with Node.js 26 LTS locally or in CI, commits or uploads artifacts, then triggers Deployer. The server never runs npm in production. That separation mirrors immutable build discipline without container orchestration overhead.

Immutable vs mutable infrastructure: which should you choose?

There is no universal winner. The right model depends on scale, compliance needs, team skills, and how painful your last drift incident was. Use the table below as a scoring sheet, not a verdict from a vendor slide deck.

CriteriaMutable infrastructureImmutable infrastructure
Initial setup timeHours — SSH and scriptsDays to weeks — Packer, IaC, CI pipelines
Drift riskHigh without strict disciplineLow at the replaced layer
Rollback speedDepends on backups and config snapshotsFast — redeploy previous artifact or AMI
Cost at small scaleLower — one VPS, manual opsHigher — build pipelines, registries, more moving parts
Security patchingIn-place apt/dnf on each serverRebuild image, roll fleet; no partial patch state
Best fitLegacy PHP, shared hosting, solo maintainerMulti-node fleets, regulated workloads, frequent releases
Laravel exampleSSH + manual Composer on live docrootDeployer releases, Docker, or AMI + ASG

For Nepal-based SMB sites — law firm portals, WooCommerce 11.1 shops, booking engines — mutable OS with immutable app releases is the sweet spot I use most often. Full VM immutability makes sense when you run multiple identical nodes behind a load balancer or need SOC-style audit trails of every system state.

Declarative vs imperative infrastructure cuts across both columns. Terraform is declarative; Ansible can be imperative playbooks against mutable hosts. GitOps pushes immutability by making Git the source of truth for desired cluster state. Chef and Puppet historically targeted mutable convergence. Know which layer each tool manages before you label the stack “immutable.”

Choose Your ModelHow many app servers?One VPSMutable OS OK2–5 nodesHybrid releasesFleet / K8sFull immutableDeployer + GitLab CIImmutable app layerDocument OS changesPacker golden AMITerraform ASG rollsZero-downtime swapsContainer imagesK8s or ECS replaceGitOps desired state
Decision guide for immutable vs mutable infrastructure: single VPS teams often hybridize; fleets benefit from full image-based replacement.

What are common mistakes when mixing immutable and mutable patterns?

The worst outcome is pretending you are immutable while still SSH-ing hotfixes onto live AMIs. Teams bake golden images, then log in to “just tweak this one nginx rule.” Six months later the launch template says one thing and production behaves another. If you allow mutable exceptions, log them in IaC within 24 hours or rebuild the image.

Another mistake is storing uploaded files on the local disk of disposable instances. Laravel storage/app on a phoenix server disappears when the VM dies. Use S3-compatible object storage or a shared NFS/EFS mount. Spatie Media Library works well when the disk driver points at cloud storage, not local on a cattle node.

Stateful sessions on local filesystems break rolling replaces too. Move sessions to Redis 8.10 or database-backed drivers before you adopt autoscaling. I standardize on Redis for Laravel queue and session on any site heading toward multiple nodes.

Mutable patterns that still make sense in 2026

  • Managed databases — RDS, Cloud SQL, or a dedicated MySQL 8.4 LTS box you patch on a schedule.
  • Developer laptops and local Docker Compose stacks.
  • Short-lived staging VMs rebuilt weekly from the same Packer template as production.
  • Legacy WordPress 7.1 on shared cPanel where you cannot replace the host.

Chef and similar config management remain valid for mutable convergence when immutability is not yet funded. The goal is documented, repeatable change — not ideology.

How do you migrate from mutable servers to immutable releases?

Migration should be incremental. Big-bang “containerize everything this sprint” fails on active client portals with payment webhooks and uploaded legal documents. Start where risk is lowest and value is highest.

  1. Inventory drift. Run the same package list and PHP module check on staging and production. Save output to a repo. Use a JSON formatter to diff CI artifacts if you export config as JSON.
  2. Externalize state. Move sessions, caches, queues, and uploads off the VM disk before you automate replacement.
  3. Immutable app deploys. Adopt Deployer or GitLab CI symlink releases even on one server. Stop editing /var/www/html directly.
  4. Document the OS baseline. Capture a Packer template or Ansible role that reproduces today’s working server. Rebuild staging from it and run your test suite.
  5. Roll production. Schedule a maintenance window or use zero-downtime infrastructure updates with a second node and load balancer if traffic demands it.

Sister sites on my shared Deployer pipeline — including Notary Kathmandu and related legal-tech properties — share CI patterns but separate release folders per domain. That limits blast radius when one deploy misconfigures .env caching.

For larger rewrites, enterprise application development engagements often include an infrastructure phase: Terraform modules, Packer pipelines, and runbooks before feature sprints resume. Terraform for IaC declares the replaceable compute layer while RDS handles mutable database patching on AWS’s schedule.

AWS publishes a clear framing of replace-over-repair in their immutable infrastructure whitepaper. It aligns with how Auto Scaling Groups and CodeDeploy hook into new launch templates rather than patching instances individually.

Monitoring and proof both models work

Immutable stacks still need mutable-adjacent observability. Log shipping, APM, and disk alerts do not care which philosophy you chose. After each roll, verify health endpoints, queue depth, and payment callback success rates. On mutable boxes, add a weekly cron that diffs installed packages against a golden manifest and alerts on mismatch.

Testing and optimization should include infrastructure tests: Terratest for Terraform modules, smoke tests after Deployer publish, and canary traffic if you run two ASG versions. Support and maintenance retainers then cover image rebuilds for CVE response instead of emergency SSH marathons.

If you are building greenfield APIs on Laravel 13 with horizontal scale in mind, pair immutable containers with API development practices: versioned endpoints, idempotent webhooks, and stateless workers. The application design and the infrastructure model should agree that nothing important lives on local disk.

Key Takeaways

  • Immutable vs mutable infrastructure is a per-layer choice: OS, platform, and application can use different models on the same project.
  • Mutable servers drift; immutable artifacts roll back cleanly because version tags and AMIs are the source of truth.
  • Single-server Laravel shops get 80% of the benefit from immutable Deployer releases without rebuilding AMIs every week.
  • Externalize sessions, uploads, and queues before you treat compute as disposable cattle.
  • Document every manual SSH change or rebuild from Packer — half-immutable stacks fail audits and incident response.
  • Scale and compliance drive full immutability; budget and team size drive sensible hybrid patterns.

People Also Ask

Is Docker immutable infrastructure?

Docker images are immutable at the digest level: once built and pushed, that layer stack does not change. Running containers are still mutable if you exec in and install packages. Production best practice is to rebuild the image and redeploy pods or containers rather than patching running instances.

Can you use Ansible with immutable infrastructure?

Yes, but the role shifts. Ansible provisions the initial Packer build or bootstraps cloud-init on first launch. It should not repeatedly converge live production servers unless you accept mutable drift. Some teams use Ansible once inside Packer, then never against running app nodes.

What is the opposite of immutable infrastructure?

Mutable infrastructure is the direct opposite: long-lived servers modified in place through SSH, package managers, and manual config edits. Configuration management tools can reduce chaos but still target changing servers rather than replacing them.

Does immutable infrastructure cost more?

Upfront tooling and CI time cost more. At scale, immutability often saves money by cutting incident hours, shortening rollbacks, and enabling smaller ops teams. On one cheap VPS, mutable management stays cheaper until drift or downtime exceeds roughly one day of engineer time per quarter.

Pick the model your team will actually maintain

Immutable vs mutable infrastructure is not a purity test. It is a bet on how much drift and downtime your business can absorb. I default to immutable application releases with Deployer and GitLab CI on PHP stacks, then add golden AMIs or containers when node count or compliance pressure demands it. If your production servers have untracked manual history, start by inventorying drift and externalizing state — not by buying a Kubernetes cluster.

Need help designing deploy pipelines, Packer templates, or a migration path for an existing Laravel or WordPress estate? Review the Court Marriage In Nepal and other portfolio projects for patterns in production, browse related posts on the blog, or contact us to discuss web development and infrastructure for your next release cycle. For background on who maintains these stacks, see about me or return to the homepage.

Frequently Asked Questions

Mutable infrastructure means you change live servers in place via SSH, package installs, and config edits. Immutable infrastructure means you build a versioned artifact or image and replace the running instance rather than patching it in place.

Configuration drift is when live servers slowly diverge from documented intent or from each other. On mutable boxes, manual apt upgrades, pecl installs, or one-off nginx tweaks accumulate until staging and production behave differently. The article describes tracing a shared php.ini chain after a troubleshooting session broke another site on the same EC2 host. Weekly package diffs against a golden manifest help catch drift before it causes TLS failures or queue worker errors.

Drift is the silent tax on mutable infrastructure. Two servers may both run Laravel 12 on PHP 8.3, yet one received an OpenSSL patch after a CVE alert and the other did not, causing TLS failures to a payment API. Mutable workflows feel fast because SSH plus apt install takes minutes, but without strict discipline every manual change creates an environment that no longer matches staging, backups, or documentation. Signs you need a reset include differing dpkg output between roles and disaster recovery that means find the backup instead of reprovision from code.

Immutable infrastructure follows a build-release-run loop: CI compiles assets and tests, tags an artifact such as ami-NEW or a container digest, then launches new instances from that tag only. On AWS, Packer bakes an AMI with PHP, Nginx, and hardening; Terraform launches an Auto Scaling Group; user data pulls the application release from S3 or a registry. When PHP versions change, you update the Packer template and roll instances. You do not apt-upgrade running boxes. Old instances retire after health checks pass, which keeps rollback as simple as redeploying the previous artifact.

There is no universal winner. Mutable suits legacy PHP, shared hosting, solo maintainers, and tight budgets because initial setup takes hours. Immutable suits multi-node fleets, regulated workloads, and frequent releases where drift incidents already hurt you, though setup takes days to weeks for Packer, IaC, and CI pipelines. For Nepal-based SMB sites such as law firm portals, WooCommerce shops, and booking engines, the article recommends mutable OS with immutable app releases as the sweet spot. Full VM immutability pays off when you run identical nodes behind a load balancer or need audit trails of every system state.

Yes, both models can coexist because immutability applies per layer, not to the entire stack. A mutable base OS image might boot from cloud-init while an immutable application layer ships as a container or tarball. Deployer 7 on a persistent EC2 box is a common hybrid: the OS stays mutable, but each deploy creates a fresh releases folder with vendor code and atomically swaps the current symlink. GitLab CI builds frontend assets with Node.js 26 LTS so the server never runs npm in production. That gives predictable application deploys without rebuilding AMIs on every release.

A golden image is a tested AMI, VM template, or container base built by Packer or a CI pipeline. It becomes the only approved way to spawn app servers.

For a single-server Laravel shop on hosting around Rs 15,000 per month (~USD 112), full immutability at the VM layer is often overkill. Application-level immutability through Deployer release directories delivers most of the benefit at lower cost.

Use Deployer 7 with GitLab CI on a persistent EC2 instance. Each deploy creates a timestamped release directory with a fresh vendor tree, runs artisan config, route, and view cache, then atomically swaps the current symlink. Rollback is dep rollback to the previous release folder. Shared dirs like storage and shared files like .env persist outside the release. PHP-FPM reloads after publish. Frontend assets build in CI with Node.js 26 LTS and ship as artifacts so production never runs npm. This is immutable application deployment on a mutable host.

No. Terraform or Bicep can declare desired cloud resources for either model. Chef, Ansible, or shell scripts can enforce mutable state on long-lived servers. Packer and container builds produce immutable artifacts. Running ansible-playbook against the same server for three years is still mutable unless you rebuild the host from scratch on every change. Declarative Terraform differs from imperative Ansible playbooks, and GitOps pushes immutability by making Git the source of truth. Know which layer each tool manages before labeling the stack immutable.

The worst mistake is pretending you are immutable while still SSH-ing hotfixes onto live AMIs. Teams bake golden images, tweak one nginx rule by hand, and six months later the launch template and production behavior diverge. If you allow mutable exceptions, log them in IaC within twenty-four hours or rebuild the image. Storing Laravel uploads on local disk of disposable instances loses data when a phoenix server dies; use S3-compatible storage or shared NFS or EFS with Spatie Media Library pointed at cloud storage. Stateful sessions on local filesystems break rolling replaces, so move sessions to Redis or database drivers before autoscaling.

Migration should be incremental, not a big-bang containerize-everything sprint on active portals with payment webhooks and uploaded documents. Start by inventorying drift: run the same package list and PHP module check on staging and production and save output to a repo. Externalize state by moving sessions, caches, queues, and uploads off the VM disk. Adopt Deployer or GitLab CI symlink releases and stop editing the live docroot directly. Document the OS baseline in Packer or Ansible, rebuild staging from it, run your test suite, then roll production during a maintenance window or with a second node and load balancer for zero downtime.

When you run one VPS with Apache or Nginx and PHP-FPM, full VM immutability is usually unnecessary. Hybrid Deployer releases are enough.

Managed databases such as RDS, Cloud SQL, or a dedicated MySQL 8.4 LTS box you patch on a schedule should stay mutable because data persistence requires scheduled maintenance, not phoenix replacement. Developer laptops and local Docker Compose stacks, short-lived staging VMs rebuilt weekly from the same Packer template as production, and legacy WordPress 7.1 on shared cPanel where you cannot replace the host also fit mutable patterns. Chef and similar config management remain valid for mutable convergence when full immutability is not yet funded. The goal is documented, repeatable change, not ideology.

Both models need observability regardless of philosophy. After each roll, verify health endpoints, queue depth, and payment callback success rates. On mutable boxes, add a weekly cron that diffs installed packages against a golden manifest and alerts on mismatch. Infrastructure tests include Terratest for Terraform modules, smoke tests after Deployer publish, and canary traffic when running two Auto Scaling Group versions. Immutable stacks still need log shipping, APM, and disk alerts. Support retainers then cover image rebuilds for CVE response instead of emergency SSH marathons across a drifted fleet.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: