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.

Nomad: Simple Workload Orchestration

By Kokil Thapa | Last reviewed: August 2026

If you are running multiple Laravel applications, background queue workers, or PHP-FPM services across several servers, managing deployments with basic scripts quickly becomes unmanageable. Nomad: Simple Workload Orchestration solves this by providing a single control plane to schedule, deploy, and heal containerized or binary workloads without the massive operational overhead of Kubernetes. For full-stack developers and agencies maintaining client infrastructure, understanding this tool bridges the gap between manual server management and enterprise-grade platform engineering.

While my primary focus remains on application architecture and Laravel development in Nepal, I frequently encounter projects where the deployment target has outgrown a single VPS but does not justify a dedicated DevOps team. In these scenarios, adopting a complex orchestrator often introduces more problems than it solves. Nomad occupies a critical middle ground. It allows you to treat your fleet of Ubuntu servers as a unified resource pool, scheduling PHP workers, Nginx reverse proxies, and Redis instances based on actual capacity rather than static assignments. This approach aligns perfectly with the pragmatic engineering philosophy required when delivering cost-effective web solutions for businesses that need reliability without cloud-native bloat.

What makes Nomad: Simple Workload Orchestration different from Kubernetes?

The distinction lies in architectural philosophy and operational weight. Kubernetes is a comprehensive platform designed to be an operating system for the cloud; it mandates containers, requires extensive CRDs, and runs dozens of internal components just to reach a ready state. Nomad is fundamentally a scheduler first. It supports containers via Docker/Podman but also treats raw binaries, Java JARs, QEMU VMs, and simple shell scripts as first-class citizens. This "multi-workload" capability is decisive for PHP shops where legacy applications may not yet be containerized or where specific compliance requirements demand bare-metal execution alongside modern microservices.

Nomad ArchitectureSingle Binary Server + AgentDocker Driver(Laravel App)Raw Exec Driver(Legacy PHP Script)Consul / Vault Integration (Optional)Kubernetes ArchitectureAPI Server + etcd + SchedulerController ManagerCloud ControllerContainer Runtime (containerd/CRI-O)Networking (CNI) + Storage (CSI)Ingress + DNS + Metrics Stack~50MB RAM Base~2GB+ RAM Base
Nomad simple workload orchestration architecture versus Kubernetes component overhead for PHP development teams

In practice, this means a three-node Nomad cluster can run comfortably on modest VPS instances (e.g., 2 vCPU / 4GB RAM each), whereas a minimally viable Kubernetes cluster often demands significantly more resources just for its control plane. For agencies billing in NPR or serving cost-sensitive markets, this efficiency translates directly to healthier project margins. Nomad also integrates natively with other HashiCorp tools like Consul for service discovery and Vault for secrets management, but unlike K8s, these remain optional external dependencies rather than hard-coded prerequisites.

How do you configure a Laravel job spec in Nomad?

Defining a workload in Nomad uses HCL (HashiCorp Configuration Language). A common mistake I see when developers transition from Docker Compose is trying to map services one-to-one without considering Nomad's "Job → Group → Task" hierarchy. A Job represents the entire application (e.g., "laravel-ecommerce"), a Group defines co-located tasks that share networking and lifecycle (e.g., "web-worker-pool"), and Tasks are the individual units of execution.

Basic Laravel Web Job Specification

Below is a production-viable starting point for a Laravel 12 application running on PHP 8.4 FPM behind Nginx. Note the explicit resource stanza; Nomad schedules based on reserved capacity, not observed usage, which prevents noisy-neighbor issues common in oversold environments.

job "laravel-shop" {
  datacenters = ["dc1"]
  type        = "service"

  group "app" {
    count = 3

    network {
      port "http" { to = 8080 }
    }

    task "nginx-php" {
      driver = "docker"

      config {
        image = "registry.example.com/shop:php8.4-v2.1.0"
        ports = ["http"]
        
        volumes = [
          "local/storage:/var/www/html/storage/app/public"
        ]
      }

      env {
        APP_ENV     = "production"
        CACHE_DRIVER = "redis"
        QUEUE_CONNECTION = "redis"
      }

      resources {
        cpu    = 500
        memory = 512
      }

      service {
        name = "laravel-shop-web"
        port = "http"
        
        check {
          type     = "http"
          path     = "/up"
          interval = "10s"
          timeout  = "2s"
        }
      }
    }
  }
}

Critical details for PHP practitioners: Always mount persistent storage explicitly if your app writes to disk (uploads, logs). Nomad tasks are ephemeral by default. Use the /up endpoint (available in Laravel 11+) for health checks instead of / to avoid triggering heavy middleware or database queries on every probe. Define environment variables via Vault templates or Nomad variables in production rather than hardcoding them in HCL files committed to Git.

Scheduling Queue Workers Separately

A pattern I have found reliable on real client projects is separating web processes from queue workers into distinct groups within the same job file. This allows independent scaling: you might need 10 web instances during peak traffic but only 3 dedicated queue workers for email processing. Workers should use the batch or system job type depending on whether they are long-running daemons (queue:work --daemon) or triggered batch processors.

When should you choose Nomad over Docker Compose or K8s?

Choosing the right orchestration layer is an architectural decision with long-term maintenance implications. The choice rarely comes down to technical superiority alone; it involves team expertise, budget, and operational maturity. Below is a comparison framework grounded in actual deployment experience rather than marketing matrices.

CriteriaDocker ComposeNomadKubernetes
Cluster SupportSingle node onlyMulti-datacenter nativeMulti-zone/cloud native
Workload TypesContainers onlyContainers, binaries, VMs, scriptsContainers primarily (VMs via KubeVirt)
Learning CurveLow (hours)Moderate (days to weeks)High (months to proficiency)
Min Control Plane RAMN/A (app only)~100–200 MB~2–4 GB minimum
Service DiscoveryDNS aliases onlyNative + Consul integrationCoreDNS + Ingress controllers
Best ForLocal dev, single-server prodSmall-medium fleets, mixed workloadsLarge-scale cloud-native platforms

Docker Compose remains excellent for local development and single-server staging environments. However, once you need high availability across multiple physical hosts or regions, Compose hits a hard wall. Nomad fills this gap precisely. If your team already understands Linux fundamentals, systemd, and basic networking—as most experienced full-stack developers do—the transition to Nomad feels like a natural extension of existing skills rather than a paradigm shift. Kubernetes becomes justified when you have dedicated platform engineers, require auto-scaling based on custom metrics at massive scale, or operate within a cloud ecosystem that provides managed K8s at reasonable cost.

Start: Need Orchestration?Multiple Physical Nodes?NoDocker ComposeYesMixed Workloads (Non-Container)?YesChoose NomadNoTeam >5 Engineers + Cloud Budget?NoChoose NomadYesConsider Kubernetes
Decision tree for evaluating Nomad simple workload orchestration suitability based on team size and workload diversity

How does Nomad handle service discovery and load balancing for PHP apps?

Service discovery is where many orchestration tutorials fail to address real-world PHP needs. Unlike Node.js or Go services that often embed their own HTTP routers, PHP-FPM typically sits behind Nginx or Caddy. Nomad integrates tightly with Consul to register healthy task instances automatically. When your Laravel job specifies a service block, Nomad registers each running container with Consul, including host IP and dynamically assigned port.

Integrating with Nginx Upstreams

Rather than hardcoding backend IPs, configure Nginx to query Consul DNS or use a template rendered by consul-template. A practical pattern I have used on legal-tech portals involves running an Nginx sidecar or gateway group that watches Consul for laravel-shop-web service changes and regenerates upstream configuration automatically. This eliminates manual reloads during deployments and ensures zero-downtime rollouts.

# Example consul-template snippet for nginx upstream
{{ range service "laravel-shop-web" }}
server {{ .Address }}:{{ .Port }} max_fails=3 fail_timeout=30s;
{{ end }}

This approach keeps your routing layer decoupled from application deployment cycles. Health checks defined in the Nomad job spec drive registration: if a Laravel container fails its /up check three times consecutively, Consul deregisters it, and Nginx stops sending traffic within seconds. This feedback loop is essential for maintaining uptime during rolling updates or when individual nodes experience hardware degradation.

What are the operational gotchas when running Nomad in production?

Theory differs from practice. After deploying Nomad across various client environments, certain recurring issues emerge that documentation glosses over. Addressing these proactively prevents 2 AM debugging sessions.

  • Disk Exhaustion from Docker Images: Nomad does not garbage-collect unused Docker images by default. On servers with limited disk (common on budget VPS plans), old image layers accumulate rapidly. Configure the Docker driver's gc.image_delay parameter or implement a cron job running docker system prune weekly. I typically set retention to 7 days on production clients.
  • Resource Overcommitment Risks: Nomad enforces hard limits only when cgroups are properly configured. Without explicit CPU/memory reservations in job specs, a single runaway PHP worker can starve neighboring tasks. Always define resources blocks conservatively and monitor actual usage via Nomad metrics or Telemetry before increasing allocations.
  • Persistent Volume Management: Nomad’s CSI support exists but is less mature than Kubernetes'. For Laravel storage, prefer host volumes with careful permission management or object storage (S3-compatible) for media files. Bind-mounting host paths works reliably but ties tasks to specific nodes unless you implement shared filesystems like NFS—which adds its own complexity.
  • Secret Sprawl Prevention: Storing database passwords in HCL files defeats the purpose of orchestration. Use Nomad Variables (built-in since 1.4) or Vault integration from day one. Migrating secrets later requires rewriting every job spec and rotating credentials simultaneously—a painful process best avoided.
  • Client Node Drain Procedures: When decommissioning a server, always run nomad node drain -enable -yes <node-id> before shutting down. This gracefully migrates allocations to other nodes respecting health checks. Simply killing the agent causes abrupt task termination and potential data corruption in stateful services.
1. Healthy Node3 Tasks Running2. Drain EnabledNew Allocs Blocked3. Tasks MigratedGraceful Shutdown4. Node Safe DownZero DowntimeCommon Drain Mistakes to AvoidSkipping drain command entirelyIgnoring stuck allocationsForcing shutdown before migration completes
Safe node draining workflow preventing downtime during Nomad simple workload orchestration maintenance

Monitoring deserves special emphasis. Nomad exposes metrics via Prometheus format at /v1/metrics. Integrate this early with Grafana dashboards tracking allocation placement failures, resource saturation, and job update durations. Reactive troubleshooting after users report slowness is far more expensive than proactive alerting on scheduling latency spikes. For teams managing DevOps automation, embedding observability into the initial cluster setup pays dividends throughout the system's lifetime.

Implementing Nomad: Simple Workload Orchestration for Your Stack

Adopting Nomad: Simple Workload Orchestration represents a pragmatic middle path for PHP and Laravel teams who have outgrown single-server deployments but cannot justify Kubernetes' operational tax. Start small: migrate non-critical batch jobs or staging environments first to build team familiarity. Validate your Docker images locally with identical drivers before pushing to production. Document your job specifications as code in version control alongside application repositories. Most importantly, resist the urge to adopt features simply because they exist; Nomad's strength lies in doing fewer things exceptionally well. If your current pain points involve coordinating deployments across 3–20 servers, handling mixed workload types, or reducing infrastructure costs while maintaining professional reliability, Nomad deserves serious evaluation. For tailored guidance on implementing orchestrated deployments for your specific Laravel or PHP architecture, reach out to discuss your project requirements.

Frequently Asked Questions

Nomad is a lightweight workload orchestrator supporting containers, binaries, and scripts without requiring Docker or complex abstractions. Unlike Kubernetes, it runs as a single binary with minimal dependencies, making it ideal for teams managing mixed workloads on limited infrastructure budgets in Nepal or elsewhere.

Nomad Community Edition is free and open source under BSL 1.1. Enterprise licenses start around USD 30,000 per year (approx NPR 4 million) for advanced features like namespaces and sentinel policies, but most small-to-medium deployments run fine on the community version without licensing costs.

Yes, Nomad excels at running raw binaries and scripts via its exec driver. I have used this to deploy Laravel Artisan workers and Symfony console commands directly on Ubuntu servers without containerization overhead, which simplifies debugging and reduces memory usage compared to wrapping every PHP process in Docker.

Three server nodes need 2 CPU cores and 4GB RAM each; client nodes require 1 core and 2GB minimum. In practice, I have run stable three-node clusters on Rs 8,000/month VPS instances for legal-tech portals, though production eCommerce sites benefit from doubling client node resources during peak traffic.

Nomad integrates natively with Consul for DNS-based service discovery and health checking. Without Consul, you lose automatic service registration but can still use Nomad's built-in service stanza with external load balancers. For Nepal-based projects avoiding extra infrastructure, I often pair Nomad with Nginx upstreams configured via template rendering.

It works well for stateless frontend tiers and queue workers, but database persistence requires careful volume management. On WooCommerce projects like Petals Nepal, we kept MySQL outside Nomad while orchestrating PHP-FPM and Redis containers inside it, achieving zero-downtime deploys without risking data loss during rescheduling events.

Use host volumes defined in client configuration files, then reference them in job specs with volume stanzas. Avoid CSI drivers unless necessary; host volumes are simpler and more reliable for single-region deployments. Always set read-only flags where possible and validate mount permissions match your application user UID/GID.

Enable mTLS between all agents, restrict ACL tokens with least-privilege policies, run clients as non-root users, and isolate workloads using cgroups or namespaces. Never expose the HTTP API publicly. On legal-tech portals handling sensitive documents, I also enforce Vault integration for secrets and disable raw_exec driver entirely.

Nomad supports broader workload types beyond containers and has superior multi-datacenter support. Docker Swarm hasn't seen major feature updates since 2023. For teams already invested in HashiCorp ecosystem tools like Vault or Terraform, Nomad offers tighter integration. Standalone container orchestration favors Swarm only if simplicity outweighs future flexibility needs.

This usually stems from mismatched file ownership between host mounts and container users, or overly restrictive AppArmor/SELinux profiles. Check task logs via nomad alloc logs, verify volume mount options include correct uid/gid mappings, and ensure the exec or docker driver has access to required binaries. Restarting the client agent after config changes helps clear stale state.

Absolutely. Use nomad job run in pipeline stages after artifact builds. Store HCL templates in repo, render variables via envsubst or consul-template during CI. On sister sites sharing Deployer 7 pipelines, we added Nomad deployment as a parallel stage, reducing release windows from twenty minutes to under three with automatic rollback on health check failure.

Expose telemetry to Prometheus via built-in statsd endpoint, track allocation failures and resource saturation through Grafana dashboards, and set alerts on leader election churn or client disconnections. Combine with structured logging forwarded to Loki or ELK. Basic setups can rely on nomad status and nomad operator scheduler-config output for quick diagnostics during incidents.

Bridge mode with port mapping suits most web apps needing isolation; host mode reduces latency for high-throughput APIs but sacrifices network separation. For Laravel backends behind Nginx reverse proxies, bridge mode with static port ranges simplifies firewall rules. Avoid CNI plugins unless running microservices meshes; they add complexity rarely justified for monolithic PHP stacks.

Define update stanzas with max_parallel, min_healthy_time, and auto_revert settings. Failed allocations trigger automatic rollback to previous stable version within seconds. Test update strategies in staging first; aggressive timings cause cascading failures under load. On production eCommerce systems, I set conservative values ensuring at least two healthy instances remain during transitions.

Choose Nomad when managing dynamic scaling, self-healing restarts, or multi-node coordination exceeds systemd capabilities. Stick with systemd for single-server static services. Use Ansible for provisioning and configuration, not runtime orchestration. If your team maintains fewer than five servers with predictable loads, Nomad may introduce unnecessary operational overhead versus traditional init systems.

Share this article

Quick Contact Options
Choose how you want to connect me: