
August 18, 2026
8 min read
Table of Contents
By Kokil Thapa | Last reviewed: August 2026
Server drift kills production reliability. When you patch live servers or install packages manually, every instance slowly diverges until debugging becomes guesswork. Adopting immutable infrastructure and golden images with Packer solves this by baking a verified, pre-configured machine image once and replacing servers entirely during updates rather than modifying them in place. This approach pairs naturally with the zero-downtime deployment workflows I use for CI/CD pipeline setups across client projects.
What is immutable infrastructure and golden images with Packer?
Immutable infrastructure treats servers as disposable artifacts. You never SSH into a production box to install PHP, tweak Nginx configs, or apply security patches. Instead, you define the entire server state in a Packer template, build a machine image (AMI, DigitalOcean snapshot, OpenStack image), verify it automatically, and deploy fresh instances from that image. When an update is needed, you build a new image and replace the old servers.
A "golden image" is simply the validated output of this process. It contains the OS, runtime (PHP 8.4, Node 22 LTS), web server, application dependencies, security hardening, and monitoring agents — all frozen at a specific point in time. On a recent legal-tech portal deployment, switching to this model reduced provisioning failures from weekly occurrences to near-zero because every server started from an identical, tested baseline.
This contrasts sharply with mutable infrastructure where Ansible or Chef runs against long-lived servers. Mutable systems work until they don't — a missed package version, a partial config merge, or a manual hotfix creates snowflakes that break unpredictably. For teams managing Laravel applications in production, immutability removes an entire class of deployment bugs.
How do you write a Packer template for a PHP-FPM golden image?
Packer uses HCL2 templates to define sources (cloud providers), builds (provisioning steps), and post-processors (tagging, export). Below is a minimal but production-viable template targeting Ubuntu 24.04 on DigitalOcean, suitable for hosting Laravel 12.x or Symfony 7.x applications. This mirrors configurations I've used for legal-tech portals requiring consistent PHP 8.4 environments.
packer {
required_plugins {
digitalocean = {
version = ">= 1.3.0"
source = "github.com/digitalocean/digitalocean"
}
}
}
variable "do_token" { type = string sensitive = true }
source "digitalocean" "php_app" {
api_token = var.do_token
image = "ubuntu-24-04-x64"
region = "sgp1"
size = "s-2vcpu-4gb"
ssh_username = "root"
snapshot_name = "php-app-golden-{{timestamp}}"
}
build {
sources = ["source.digitalocean.php_app"]
provisioner "shell" {
script = "scripts/install-php.sh"
}
provisioner "file" {
source = "configs/www.conf"
destination = "/tmp/www.conf"
}
provisioner "shell" {
inline = [
"mv /tmp/www.conf /etc/php/8.4/fpm/pool.d/www.conf",
"systemctl disable php8.4-fpm",
"apt-get clean",
"rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*"
]
}
} Key provisioning principles
- Disable services at build time. Systemd units like PHP-FPM should be disabled so they start cleanly on first boot via cloud-init or your orchestrator, avoiding stale PID files baked into the image.
- Clean caches aggressively. APT lists, temp files, and log artifacts add hundreds of MBs to image size. Smaller images deploy faster and reduce storage costs.
- Pin explicit versions. Use
apt-get install php8.4-fpm=8.4.*or Ondřej Surý’s PPA with pinned releases. Never rely onlatesttags in production images. - Validate inside the build. Add a final provisioner that runs
php -v,nginx -t, or application-specific smoke tests. Fail the build if validation fails — never publish unverified artifacts.
How does immutable deployment compare to mutable configuration management?
Choosing between immutable and mutable approaches depends on team size, change frequency, and tolerance for drift. Here's a practical comparison based on real deployments for eCommerce and legal-tech clients:
| Criteria | Immutable (Packer) | Mutable (Ansible/Chef) |
|---|---|---|
| Configuration drift | Eliminated by design | Accumulates over time |
| Rollback speed | Instant (revert image tag) | Slow (reverse playbook) |
| Build time | 10–30 min per image | N/A (config applied at deploy) |
| Deployment complexity | Simple artifact swap | Idempotency edge cases |
| Debugging production | Reproduce locally from same image | Guess current state |
| Best for | Stable runtimes, regulated apps | Rapidly changing dev environments |
For business-critical systems like payment-processing eCommerce stores or legal document portals, immutability wins decisively. The upfront cost of building proper Packer templates pays back within weeks through eliminated "works on my machine" incidents and faster incident recovery. Mutable tooling still has value for local development environments or exploratory prototypes where rebuild cycles would slow iteration.
How do you integrate Packer into a GitLab CI pipeline for automated builds?
Manual Packer builds defeat the purpose. Automate image creation on every merge to your infrastructure repository. This ensures the golden image stays synchronized with application requirements. For teams using DevOps automation practices, this integration is foundational.
- Store secrets securely. Use GitLab CI/CD variables (masked, protected) for cloud provider tokens. Never commit credentials to the repository.
- Tag images semantically. Include git SHA, timestamp, and version:
php-app-golden-v1.2.3-a1b2c3d-20260818. This enables precise rollback and audit trails. - Run validation tests. Use InSpec, Goss, or custom shell scripts inside the Packer build to verify PHP extensions, open ports, file permissions, and service states before marking the image as ready.
- Promote artifacts explicitly. Tag verified images as
candidate, then promote toproductionafter staging validation. Never auto-deploy untested images.
stages:
- validate
- build
- test
packer-build:
stage: build
image: hashicorp/packer:1.11
script:
- packer init .
- packer validate .
- packer build -var "do_token=$DO_TOKEN" .
only:
- main
tags:
- docker
image-test:
stage: test
script:
- ./scripts/verify-golden-image.sh $CI_COMMIT_SHA
needs:
- packer-build This pipeline runs on every merge to main. The build job initializes plugins, validates syntax, and executes the full provisioning sequence. The test job spins up a temporary instance from the new image and runs acceptance checks. Only passing images get promoted for production deployment.
What are common mistakes when adopting immutable infrastructure and golden images with Packer?
I've seen teams adopt immutability incorrectly and create worse problems than they solved. Avoid these pitfalls:
- Baking secrets into images. API keys, database passwords, and TLS private keys must never exist in the golden image. Inject them at boot via environment variables, Vault, or cloud provider secret managers. Images are shared artifacts; treat them as potentially public.
- Skipping validation. An untested golden image is just a frozen bug. Always include automated checks that verify critical functionality before publishing. On one project, we caught a missing PHP extension during the build phase that would have caused silent queue worker failures in production.
- Ignoring cleanup. Failing to remove APT caches, log files, SSH host keys, and cloud-init artifacts bloats images and leaks metadata. Run thorough cleanup as the final provisioning step.
- Treating images as static forever. Golden images expire. Schedule regular rebuilds (weekly or monthly) to incorporate security patches even when application code hasn't changed. Subscribe to CVE feeds for your base OS and runtime.
- Over-provisioning. Don't bake application code into the golden image unless it changes infrequently. Keep the image focused on platform dependencies; deploy application artifacts separately via your existing CI/CD workflow. This keeps rebuild cycles fast and images reusable across multiple services.
Making the shift to immutable infrastructure and golden images with Packer
Start small. Pick one non-critical service — perhaps a staging environment or internal tool — and build your first Packer template. Validate thoroughly before trusting it for production traffic. Document the build process and share ownership across the team; immutability fails when only one person understands the image pipeline. Budget NPR 50,000–150,000 (~USD 375–1,125) for initial setup if hiring expertise, though the long-term operational savings justify this investment quickly.
The discipline required for immutable infrastructure and golden images with Packer pays compounding returns. Fewer production incidents, faster scaling, predictable rollbacks, and auditable deployments become your baseline rather than aspirational goals. For teams ready to modernize their deployment practices, this is the foundation everything else builds on. Reach out via the contact page if you need hands-on guidance implementing this for your PHP or Laravel stack.

