
September 12, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
Your server works in staging but breaks in production because nobody documented the manual steps. That gap is exactly where Declarative vs Imperative Infrastructure stops being academic and starts costing you sleep. Both models automate provisioning, but they answer different questions. Declarative code describes the end state you want. Imperative code lists the commands to run. On real client projects I maintain with infrastructure as code (IaC), the choice between these models affects drift, rollbacks, and who can safely change production.
What Is the Difference Between Declarative and Imperative Infrastructure?
Think of declarative infrastructure as a recipe card that says “serve four bowls of soup.” Imperative infrastructure is the cook shouting “chop onions, boil water, stir for ten minutes.” Both feed people. Only one survives when the cook changes mid-shift.
Declarative systems store what should exist: three app servers, one load balancer, TLS on port 443. The tool compares live reality against that definition and plans changes. Imperative systems store how to change things: run `apt upgrade`, copy this file, restart PHP-FPM. Order matters. Skipping a step breaks the chain.
The distinction matters because infrastructure failures rarely come from bad intentions. They come from untracked manual edits, wrong command order, or a deploy script that assumes yesterday’s disk layout. Understanding both models helps you pick the right default for cloud resources versus application releases.
Core vocabulary you will hear in every review
- Desired state: the target configuration your code declares.
- Drift: when live infrastructure no longer matches declared state.
- Idempotency: running the same operation twice produces the same result.
- Reconciliation: the tool’s loop that closes the gap between actual and desired.
- Mutable vs immutable: patching servers in place versus replacing them from golden images.
For background on keeping state stable over time, read about idempotent infrastructure principles. Idempotency is the property both paradigms chase, but declarative tools enforce it at the plan layer.
How Does Declarative Infrastructure Work in Practice?
Declarative tools treat your repo as the source of truth. You commit a Terraform module, open a pull request, review the plan output, then apply. The plan shows creates, updates, and destroys before anything touches production. That review step is the safety rail small teams often skip when they rely on imperative scripts alone.
On shared EC2 infrastructure I maintain for legal-tech sister sites, Terraform-style declarative modules define VPC rules, security groups, and DNS records. Application code still deploys through GitLab CI and Deployer, which is imperative at the release layer. The split is intentional. Cloud topology changes rarely. Application code changes daily.
Example: declarative Nginx + TLS on Ubuntu
A minimal Terraform-style declaration might look like this:
resource "aws_instance" "app" {
ami = "ami-ubuntu-2404-lts"
instance_type = "t3.small"
tags = {
Name = "laravel-app-prod"
Environment = "production"
}
}
resource "aws_security_group_rule" "https" {
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.web.id
} You never SSH in to “create a security group rule.” Terraform computes the diff. If someone adds port 22 manually, the next plan flags drift. That feedback loop is why declarative IaC pairs well with GitOps-style review workflows even outside Kubernetes.
For a deeper walkthrough, see the guide on Terraform infrastructure as code and AWS CloudFormation on AWS. Official Terraform docs at developer.hashicorp.com/terraform/docs remain the canonical reference for resource syntax and state backends.
Where declarative models shine
- Cloud networking, IAM, databases, and load balancers with clear resource APIs.
- Multi-environment parity where staging must mirror production topology.
- Compliance audits that need a readable desired-state document.
- Teams that want plan/apply gates in CI before any change lands.
Declarative code also supports modules. Reusable blocks reduce copy-paste errors across client projects. That pattern mirrors how I reuse Deployer recipes, but at the cloud layer instead of the app layer.
How Does Imperative Infrastructure Work in Practice?
Imperative automation runs commands. Ansible playbooks, Bash deploy scripts, Capistrano, and Deployer 7 all fit this bucket. You define tasks in sequence: install packages, clone Git, run Composer, symlink the release, reload PHP-FPM. Each step assumes the previous one succeeded.
I use imperative deploys daily on Laravel applications. Deployer 7 performs zero-downtime symlink swaps on Ubuntu with Apache and PHP-FPM 8.4. The script is imperative by design. It must run steps in order. You cannot declare “the site is deployed” and expect Deployer to infer Git fetch semantics.
Example: imperative Laravel deploy task
task('deploy:vendors', function () {
run('cd {{release_path}} && {{bin/composer}} install --no-dev --prefer-dist -o');
});
task('deploy:symlink', function () {
run('ln -sfn {{release_path}} {{deploy_path}}/current');
run('sudo systemctl reload php8.4-fpm');
}); That reload step matters. Opcache keeps old bytecode until PHP-FPM restarts or reloads. A declarative “package installed” flag would miss the runtime effect. Imperative scripts express operational reality directly.
Configuration management tools like Ansible blend both worlds. Playbooks list tasks imperatively, but modules aim for idempotent side effects. The Chef automation guide covers a similar model for mutable servers. Ansible’s official documentation at docs.ansible.com explains module idempotency flags that prevent double-install damage.
Imperative workflows excel when the operation is procedural. Database migrations, cache warmers, and payment-gateway webhook re-registration do not map cleanly to static resource blocks. You run them once, in order, with logging.
Which Model Should You Choose: Declarative or Imperative?
Neither side wins every fight. Mature teams usually declare cloud foundations and imperative application releases. Trying to declare every Composer post-install hook in Terraform creates YAML soup. Running all cloud networking from Bash creates undebuggable snowflake servers.
| Criteria | Declarative (Terraform, CloudFormation, Bicep) | Imperative (Ansible, Bash, Deployer, CI scripts) |
|---|---|---|
| Primary question | What should exist? | What commands should run? |
| Drift detection | Built into plan/apply cycle | Manual unless you add checks |
| Learning curve | Steeper upfront (state, providers) | Lower if you already know shell |
| Best fit | Cloud resources, DNS, IAM, networks | App deploys, migrations, one-off fixes |
| Rollback story | Revert commit + apply (or state rollback) | Run previous script version or symlink back |
| Idempotency | Engine enforces at resource level | You enforce per task/module |
| Team size fit | Strong for multi-env teams with review gates | Strong for solo devs and small agencies |
Verdict: start declarative for anything that touches billing—VPCs, RDS instances, S3 buckets, IAM roles. Keep imperative scripts for application lifecycle tasks you already understand. On a booking platform like Adventure Third Pole Trek, Laravel + Livewire code ships through imperative CI while DNS and TLS stay declarative.
If you manage servers for clients without a dedicated platform team, Linux system administration often blends both: declarative firewall rules, imperative package pinning for PHP multi-version hosts.
Decision checklist before you standardise
- Does the API expose resources with stable IDs? Lean declarative.
- Is the task a one-time migration or data backfill? Lean imperative.
- Do auditors need a human-readable desired-state file? Lean declarative.
- Does the step require live service reload semantics? Lean imperative.
- Will non-engineers run the change? Prefer declarative plans with guardrails.
What Are Common Mistakes When Mixing Declarative and Imperative Tools?
The worst failures I see are not picking the wrong paradigm. They are using both without boundaries. Someone runs manual `aws ec2 modify-instance-attribute` after Terraform created the host. The next apply fights human changes. Or a declarative Kubernetes manifest tries to run database seeds on every sync. That is an imperative job dressed in YAML.
Mistake 1: manual changes outside state
SSH edits bypass drift detection. Document emergency fixes and backport them into IaC within 24 hours. For sites on shared pipelines like Notary Kathmandu, I treat emergency SSH as debt with a ticket, not a workflow.
Mistake 2: storing secrets in declarative files
Never commit plaintext database passwords in Terraform. Use environment-specific secret stores or CI variables. Validate JSON configs locally with a JSON formatter before they enter the pipeline. Declarative repos are cloned widely. Assume leaks.
Mistake 3: ignoring immutable alternatives
Declarative patching of long-lived VMs still accumulates entropy. For some workloads, immutable golden images with Packer reduce patch drift entirely. You declare the image version, not every apt package history.
Mistake 4: skipping plan review in CI
Declarative without review is just faster breakage. Wire `terraform plan` or equivalent into merge requests. Pair that with zero-downtime Terraform updates when load balancers and health checks are involved.
Jenkins users can mirror the same guardrails. A declarative Jenkins pipeline for app builds does not replace IaC plans. It complements them. Pipeline-as-code is declarative for CI; deploy scripts inside it may still be imperative.
How Do Real Production Stacks Combine Both Models in 2026?
A typical Laravel production stack on Ubuntu 24 in 2026 looks like this. Declarative layers provision the EC2 instance, Elastic IP, Route53 records, security groups, and RDS MySQL 8.4. Imperative layers install the release via Deployer, run `php artisan migrate --force`, and reload PHP-FPM 8.4. Redis 8.10 caching config may live in declarative env vars or imperative `.env` templating depending on team taste.
WordPress and WooCommerce shops follow the same split. Declarative DNS and CDN rules. Imperative plugin updates tested on staging first. For WooCommerce 11.1 on WordPress 7.1, I avoid declaring every plugin file in Terraform. That fight is not worth the module complexity.
Multi-language IaC tools blur the line. Pulumi lets you write IaC in PHP or JavaScript, which feels imperative in syntax but compiles to declarative resource graphs. Azure Bicep and Crossplane push declarative models into Kubernetes-native controllers. The paradigm label matters less than whether the engine reconciles state automatically.
Hosting choices affect how far you can go declarative. Shared cPanel-style hosting often limits you to imperative FTP uploads and manual cron edits. Managed VPS or EC2 unlocks full IaC. If you are choosing providers, factor that into domain registration and hosting decisions early. Moving later triggers website migration work you could avoid.
After launch, support and maintenance retainers should include quarterly drift audits. Run plan-only jobs. Compare cron paths after Deployer symlink swaps. Stale cron paths are a classic imperative failure I still catch on long-running Nepali client servers.
For enterprise apps with strict uptime targets, pair declarative networking with imperative blue-green deploys. Read GitOps for infrastructure vs application GitOps to see where pull-based sync helps and where push-based deploys stay simpler.
Key Takeaways
- Declarative infrastructure defines desired state; the tool reconciles drift through plan and apply cycles.
- Imperative infrastructure runs ordered commands—ideal for deploys, migrations, and service reloads.
- Declare cloud foundations (networks, IAM, databases); script application releases and one-off fixes.
- Manual SSH edits without IaC backport create undeclared drift that the next apply may destroy.
- Hybrid stacks are industry normal in 2026—Terraform plus Deployer is a sane default for Laravel on Ubuntu.
- Review declarative plans in CI before apply; treat emergency manual fixes as debt with a backport ticket.
People Also Ask
Is Terraform declarative or imperative?
Terraform is declarative. You describe resources and their properties in HCL. The Terraform engine builds a dependency graph, compares it to stored state, and produces a plan of changes. You do not script individual API calls in sequence. Provisioning hooks exist, but the core model is desired-state reconciliation.
Is Ansible declarative or imperative?
Ansible playbooks are written imperatively as ordered tasks, but many modules behave declaratively by checking current state before acting. A task might say “install nginx,” yet the module only installs if nginx is missing. That hybrid feel confuses beginners, but it is why Ansible works well for mutable server configuration.
Can you use declarative and imperative tools together?
Yes, and most production teams should. A common pattern declares cloud infrastructure with Terraform, configures the OS with Ansible, and deploys application code with CI plus Deployer or similar. Draw a hard boundary: never manage the same resource attribute from both toolchains without a documented owner.
Which is better for small teams in Nepal?
Small teams with limited DevOps time often start imperative because shell scripts and Deployer match existing PHP workflows. As client count grows, declarative DNS and firewall rules pay off quickly. You do not need a platform engineering department to version-control a Terraform module for TLS and security groups. Start hybrid, not purist.
Pick the Right Default for Your Next Deploy
Declarative vs Imperative Infrastructure is not a loyalty test. It is a boundary decision. Declare anything that should survive staff turnover and show up in audit logs. Script anything that touches live runtime behaviour and must run once per release. If your stack feels fragile after every deploy, map each failure to the layer it belongs in—cloud, config, or app—and fix the model there.
Need help untangling a mixed pipeline on Ubuntu, Laravel, or WordPress? Contact us for a practical infrastructure review. You can also browse portfolio projects that run declarative cloud layers with imperative zero-downtime deploys, or read more on the blog about enterprise application development and long-term testing and optimization for production systems.
Frequently Asked Questions
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.

