Skip to content

Nomad Deployment Guide ​

Deploying an application to Nomad with pfy: what needs to be installed, how project.yaml aliases point at a deployment, and the day-to-day commands for releasing, planning, rolling back, and running the deploy from CI. The flag reference is on nomad.

Jobs render from a spec

pfy nomad deploy renders the full job from the Manager-stored deployment spec (render → validate → plan → run). The in-place image/tag rewrite is deprecated and runs only behind --legacy-rewrite; see the nomad command reference. For background on the rendering model, start from Generated Artifacts.

Overview ​

The pfy nomad commands cover:

  • Rendering the full Nomad job (ports, health checks, migration prestart, secrets templates, scaling policy) from the deployment spec stored in the Manager
  • Validating job configurations before deployment
  • Integrating with project version management (the image tag)
  • Providing dry-run and plan-only modes for safety
  • Applying an application's whole stack, data stores first, with --stack

Prerequisites ​

1. Install Nomad CLI ​

The pfy nomad commands require the Nomad CLI to be installed and accessible:

bash
# macOS
brew install nomad

# Linux: pick a release from https://releases.hashicorp.com/nomad/
NOMAD_VERSION=1.7.0
wget "https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_linux_amd64.zip"
unzip "nomad_${NOMAD_VERSION}_linux_amd64.zip"
sudo mv nomad /usr/local/bin/

# Verify installation
nomad version

2. Configure Nomad Connection ​

Set up Nomad connection via environment variables:

bash
export NOMAD_ADDR=http://nomad.example.com:4646
export NOMAD_TOKEN=your-nomad-token  # If ACLs are enabled

3. Set up the project and environment aliases ​

bash
# Initialize project configuration
pfy init --name my-project --language go

# Sync from the Manager; seeds one env alias per tenant into project.yaml
pfy sync --project my-project

# Or record/adjust aliases explicitly
pfy env add prod --tenant prod --application my-app --project my-project

The --env <alias> flag on every deploy/render command resolves through these project.yaml aliases; see env. An alias must carry an application_id for the deploy commands to use it; the aliases pfy sync seeds carry only tenant_id, so add the application with pfy env add (or pfy env link) before deploying, or pass --app <application-id>.

4. (Optional) Configure the rendered artifact path ​

Add the nomad_file path to your project.yaml; this is where the rendered job is written (and what pfy deploy diff compares against):

yaml
ci:
  type: github
  language: go
  registry: ghcr.io
  image_name: my-project
  nomad_file: nomad/my-app.nomad.hcl # Where the rendered job lands

With this configured, pfy nomad deploy --env prod … writes the rendered job there before applying it; otherwise pass --out <file>.

Basic Deployment Workflow ​

1. Update Version ​

First, update your project version:

bash
# Bump patch version (e.g., 1.2.3 -> 1.2.4)
pfy version bump --part patch

# Or set a specific version
pfy version set --version 1.3.0

# Check current version
pfy version show

2. Deploy to Nomad ​

Deploy using the project version as the image tag:

bash
pfy nomad deploy --env prod --use-project-version --yes

This will:

  1. Resolve the prod alias to its (application, environment) deployment
  2. Render the full Nomad job from the deployment spec, with the image tag from project.yaml
  3. Write it to ci.nomad_file (or --out), validate it, and run it

Without --yes, the deploy runs nomad job plan as a review step before nomad job run.

Advanced Usage ​

Explicit Tag ​

bash
pfy nomad deploy --env prod --tag v1.2.3 --yes

The image itself comes from the deployment spec (pfy deployment get --env prod); --tag supplies the release tag appended to it.

Safe Deployment with Planning ​

Preview changes before deploying:

bash
# Render + validate + plan only
pfy nomad deploy --env prod --tag v1.2.3 --plan-only

# Review the output, then deploy
pfy nomad deploy --env prod --tag v1.2.3 --yes

Dry Run ​

Print the rendered job without writing, validating, or deploying:

bash
pfy nomad deploy --env prod --tag v1.2.3 --dry-run

Skip Validation ​

bash
pfy nomad deploy --env prod --tag v1.2.3 --skip-validation --yes

Dedicated tenants and backend services ​

bash
# Deploy the tenant-dedicated job instead of the shared one
pfy nomad deploy --env prod --tenant acme --tag v1.2.3 --yes

# Deploy one backend service of the application's stack
pfy nomad deploy --env prod --service api --tag v1.2.3 --yes

Whole-stack deploys ​

Apply managed data stores, backup jobs, and services in dependency order, gating each stage on health:

bash
pfy nomad deploy --stack --env prod --app my-app --project my-saas --tag v1.2.3 --yes

See Stack deploys for the --only-data, --no-wait, --wait-timeout and --install knobs.

Multi-Environment Deployments ​

One deployment spec exists per (application, environment); the --env alias picks it. There is no per-environment job file to maintain; the render is the artifact.

Development Environment ​

bash
pfy nomad deploy --env dev --tag dev-latest --yes

Staging Environment ​

bash
pfy nomad deploy --env staging --tag v1.2.3 --yes

Production Environment ​

bash
# Always use plan-only first in production
pfy nomad deploy --env prod --tag v1.2.3 --plan-only

# Review the plan carefully, then deploy
pfy nomad deploy --env prod --tag v1.2.3 --yes

CI/CD Integration ​

GitHub Actions Example ​

yaml
name: Deploy to Nomad

on:
  push:
    tags:
      - "v*"

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install pfy CLI
        run: |
          PFY_VERSION=$(curl -fsSL https://api.github.com/repos/ProductifyFW/cli/releases/latest | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
          curl -fsSLO "https://github.com/ProductifyFW/cli/releases/download/v${PFY_VERSION}/pfy_${PFY_VERSION}_linux_amd64.tar.gz"
          curl -fsSLO "https://github.com/ProductifyFW/cli/releases/download/v${PFY_VERSION}/checksums.txt"
          grep "pfy_${PFY_VERSION}_linux_amd64.tar.gz" checksums.txt | sha256sum -c -
          tar -xzf "pfy_${PFY_VERSION}_linux_amd64.tar.gz" pfy
          sudo mv pfy /usr/local/bin/pfy

      - name: Deploy to staging
        env:
          NOMAD_ADDR: ${{ secrets.NOMAD_ADDR }}
          NOMAD_TOKEN: ${{ secrets.NOMAD_TOKEN }}
          PFY_MANAGER_URL: ${{ secrets.PFY_MANAGER_URL }}
          PFY_MANAGER_TOKEN: ${{ secrets.PFY_MANAGER_TOKEN }}
        run: |
          # Extract version from tag
          VERSION=${GITHUB_REF#refs/tags/v}

          # Deploy to staging
          pfy --ci nomad deploy --env staging --tag v$VERSION --yes

      - name: Deploy to Production
        if: github.event_name == 'release'
        env:
          NOMAD_ADDR: ${{ secrets.NOMAD_ADDR }}
          NOMAD_TOKEN: ${{ secrets.NOMAD_TOKEN }}
          PFY_MANAGER_URL: ${{ secrets.PFY_MANAGER_URL }}
          PFY_MANAGER_TOKEN: ${{ secrets.PFY_MANAGER_TOKEN }}
        run: |
          VERSION=${GITHUB_REF#refs/tags/v}
          pfy --ci nomad deploy --env prod --tag v$VERSION --yes

GitLab CI Example ​

This assumes pfy is installed in the job image (see Configuration for an install snippet) and that the pipeline writes the release version to a .version file in an earlier stage.

yaml
deploy:staging:
  stage: deploy
  script:
    - VERSION=$(cat .version)
    - pfy --ci nomad deploy --env staging --tag v$VERSION --yes
  environment:
    name: staging
  only:
    - main

deploy:production:
  stage: deploy
  script:
    - VERSION=$(cat .version)
    - pfy --ci nomad deploy --env prod --tag v$VERSION --plan-only
    # Requires manual approval in GitLab; the approved job then runs with --yes
  environment:
    name: production
  only:
    - tags
  when: manual

Workflow Examples ​

Complete Release Workflow ​

bash
# 1. Bump version
pfy version bump --part minor

# 2. Commit version change
git add project.yaml
git commit -m "Bump version to $(pfy version show)"

# 3. Create git tag
git tag v$(pfy version show)
git push && git push --tags

# 4. Deploy to staging
pfy nomad deploy --env staging --use-project-version --yes

# 5. Test staging...

# 6. Deploy to production (with plan)
pfy nomad deploy --env prod --use-project-version --plan-only

# 7. Review plan, then deploy
pfy nomad deploy --env prod --use-project-version --yes

Hotfix Workflow ​

bash
# 1. Create hotfix branch
git checkout -b hotfix/critical-fix

# 2. Make fixes and bump patch version
pfy version bump --part patch

# 3. Deploy directly to production with planning
pfy nomad deploy --env prod --use-project-version --plan-only

# 4. After review, deploy
pfy nomad deploy --env prod --use-project-version --yes

# 5. Merge back to main
git checkout main
git merge hotfix/critical-fix

Rollback Workflow ​

bash
# 1. Find previous version
git log --oneline project.yaml

# 2. Deploy previous version's image tag
pfy nomad deploy --env prod --tag v1.2.2 --yes

# Or update project.yaml and use project version
pfy version set --version 1.2.2
pfy nomad deploy --env prod --use-project-version --yes

Rolling back the image tag does not roll back spec changes; inspect those with pfy deployment get --env prod and pfy deploy diff --env prod.

Troubleshooting ​

Nomad Connection Issues ​

bash
# Verify Nomad connection
nomad status

# Check environment variables
echo $NOMAD_ADDR
echo $NOMAD_TOKEN

Validation Failures ​

bash
# Validate the rendered job file
pfy nomad validate -f nomad/my-app.nomad.hcl

# Or use Nomad directly
nomad job validate nomad/my-app.nomad.hcl

Image Not Found ​

Ensure the image exists in your registry:

bash
# Check if image:tag exists
docker pull ghcr.io/acme/myapp:v1.2.3

Deployment Failures ​

bash
# Check Nomad job status
nomad job status my-app

# View allocations
nomad alloc logs <alloc-id>

# Check events
nomad alloc status <alloc-id>

Best Practices ​

1. Always Plan Before Production ​

bash
# Never deploy to production without planning first
pfy nomad deploy --env prod --tag v1.0.0 --plan-only

2. Use Project Version Management ​

bash
# Keep image tags in sync with project.yaml
pfy nomad deploy --env prod --use-project-version --yes

3. Tag Docker Images Properly ​

bash
# Use semantic versioning for tags
pfy nomad deploy --env prod --tag v1.2.3 --yes

# Avoid using 'latest' in production

4. Keep the spec in the Manager, the render in git ​

The deployment spec (resources, ports, health checks, scaling) lives on the Manager's Deployment entity; edit it with pfy deployment set rather than hand-editing rendered HCL, and commit the rendered artifact so pfy deploy diff can catch drift.

5. Automate in CI/CD ​

Run the deploy commands from the CI/CD pipeline so every release goes through the same steps.

6. Document Deployments ​

bash
# Use git tags and commit messages
git tag -a v1.2.3 -m "Release v1.2.3: New features and bug fixes"

See Also ​