State Examples

This page provides complete, production-ready state examples for deploying Nebula with saltext-nebula.

Basic Deployment State

A minimal state for deploying Nebula to a node:

# /srv/salt/nebula/init.sls

# Deploy certificates from master
nebula_certificates:
  nebula.certificates_present:
    - name: nebula_certs_{{ grains['id'] }}
    - auto_renew: true
    - renewal_threshold_days: 30

# Get detected paths
{% set nebula = salt['nebula.detect_paths']() %}
{% set config = salt['nebula.build_config']() %}

# Ensure config directory exists
nebula_config_dir:
  file.directory:
    - name: {{ nebula.config_dir }}
    - user: {{ nebula.user }}
    - group: {{ nebula.group }}
    - mode: {{ nebula.dir_mode }}
    - makedirs: True

# Generate configuration from pillar
nebula_config:
  file.serialize:
    - name: {{ nebula.config_file }}
    - dataset: {{ config | tojson }}
    - formatter: yaml
    - user: {{ nebula.user }}
    - group: {{ nebula.group }}
    - mode: {{ nebula.file_mode }}
    - require:
      - file: nebula_config_dir

# Enable the service
nebula_service_enable:
  module.run:
    - nebula.service_enable: []

# Restart on changes
nebula_service:
  module.run:
    - nebula.service_restart: []
    - onchanges:
      - file: nebula_config
      - nebula: nebula_certificates
    - require:
      - module: nebula_service_enable

Complete Deployment with Package Installation

A comprehensive state that handles package installation across platforms:

# /srv/salt/nebula/init.sls

#
# Package Installation
#

{% if grains['os_family'] == 'Debian' %}
nebula_package:
  pkg.installed:
    - name: nebula

{% elif grains['os_family'] == 'RedHat' %}
nebula_package:
  pkg.installed:
    - name: nebula

{% elif grains['os_family'] == 'Alpine' %}
nebula_package:
  pkg.installed:
    - name: nebula

{% elif grains['os'] == 'Windows' %}
nebula_package:
  chocolatey.installed:
    - name: nebula
    - require:
      - chocolatey: chocolatey_bootstrap

chocolatey_bootstrap:
  chocolatey.bootstrapped: []

{% endif %}

#
# User and Group Setup (Linux only)
#

{% if grains['kernel'] != 'Windows' %}
nebula_group:
  group.present:
    - name: nebula
    - system: True

nebula_user:
  user.present:
    - name: nebula
    - system: True
    - gid: nebula
    - home: /etc/nebula
    - shell: /sbin/nologin
    - createhome: False
    - require:
      - group: nebula_group
{% endif %}

#
# Certificate Deployment
#

nebula_certificates:
  nebula.certificates_present:
    - name: nebula_certs_{{ grains['id'] }}
    - minion_id: {{ grains['id'] }}
    - auto_renew: true
    - renewal_threshold_days: 30
    - backup_old_certs: true
    - validate_after_deploy: true
{% if grains['kernel'] != 'Windows' %}
    - require:
      - pkg: nebula_package
      - user: nebula_user
{% else %}
    - require:
      - chocolatey: nebula_package
{% endif %}

#
# Certificate Information Display
#

show_certificate_info:
  nebula.certificate_info:
    - name: cert_info_{{ grains['id'] }}
    - require:
      - nebula: nebula_certificates

#
# Configuration
#

{% set nebula = salt['nebula.detect_paths']() %}
{% set config = salt['nebula.build_config']() %}

nebula_config_dir:
  file.directory:
    - name: {{ nebula.config_dir }}
    - user: {{ nebula.user }}
    - group: {{ nebula.group }}
    - mode: {{ nebula.dir_mode }}
    - makedirs: True

nebula_config_file:
  file.serialize:
    - name: {{ nebula.config_file }}
    - dataset: {{ config | tojson }}
    - formatter: yaml
    - user: {{ nebula.user }}
    - group: {{ nebula.group }}
    - mode: {{ nebula.file_mode }}
    - require:
      - file: nebula_config_dir
      - nebula: nebula_certificates

nebula_config_validate:
  module.run:
    - nebula.validate_config: []
    - onchanges:
      - file: nebula_config_file

#
# TUN Module (Linux only, for containers)
#

{% if grains['kernel'] == 'Linux' and grains.get('virtual', 'physical') != 'physical' %}
tun_module_config:
  file.managed:
    - name: /etc/modules-load.d/tun.conf
    - contents: tun
    - mode: 0644

tun_module_load:
  kmod.present:
    - name: tun
{% endif %}

#
# Service Management
#

nebula_service_enable:
  module.run:
    - nebula.service_enable: []
    - require:
      - file: nebula_config_file

nebula_service:
  module.run:
    - nebula.service_restart: []
    - onchanges:
      - file: nebula_config_file
      - nebula: nebula_certificates
    - require:
      - module: nebula_service_enable
      - module: nebula_config_validate

Lighthouse-Specific State

Additional configuration for lighthouse nodes:

# /srv/salt/nebula/lighthouse.sls

include:
  - nebula

# Lighthouses need a fixed port in their firewall
{% if grains['os_family'] == 'Debian' %}
lighthouse_firewall:
  ufw.allow:
    - name: nebula-lighthouse
    - proto: udp
    - port: 4242

{% elif grains['os_family'] == 'RedHat' %}
lighthouse_firewall:
  firewalld.present:
    - name: nebula-lighthouse
    - protoports:
      - 4242/udp
{% endif %}

Beacon Configuration State

Deploy the certificate expiration beacon:

# /srv/salt/nebula/beacon.sls

nebula_beacon_config:
  file.managed:
    - name: /etc/salt/minion.d/nebula-beacon.conf
    - contents: |
        beacons:
          nebula:
            - interval: 86400
            - renewal_threshold_days: 30
    - mode: 0644

restart_minion_for_beacon:
  cmd.run:
    - name: 'salt-call service.restart salt-minion'
    - bg: true
    - onchanges:
      - file: nebula_beacon_config

Orchestration: Generate All Certificates

An orchestration state to generate certificates for all Nebula hosts:

# /srv/salt/orch/nebula_certs.sls

{% set nebula_hosts = salt.saltutil.runner('pillar.show_pillar', minion='*').get('nebula', {}).get('hosts', {}) %}

{% for host in nebula_hosts %}
generate_cert_{{ host }}:
  salt.runner:
    - name: nebula.get_certificate
    - minion_id: {{ host }}
{% endfor %}

Run with:

salt-run state.orchestrate orch.nebula_certs

Orchestration: Full Deployment

Deploy Nebula to all configured hosts:

# /srv/salt/orch/nebula_deploy.sls

# First, ensure all certificates are generated
generate_certificates:
  salt.runner:
    - name: state.orchestrate
    - arg:
      - orch.nebula_certs

# Deploy to lighthouses first
deploy_lighthouses:
  salt.state:
    - tgt: 'G@nebula:is_lighthouse:True'
    - tgt_type: compound
    - sls:
      - nebula.lighthouse
    - require:
      - salt: generate_certificates

# Then deploy to all other hosts
deploy_clients:
  salt.state:
    - tgt: 'G@nebula:hosts:*'
    - tgt_type: compound
    - sls:
      - nebula
    - require:
      - salt: deploy_lighthouses

# Verify connectivity
verify_mesh:
  salt.function:
    - name: nebula.test_connectivity
    - tgt: 'G@nebula:hosts:*'
    - tgt_type: compound
    - require:
      - salt: deploy_clients

Cleanup/Removal State

Completely remove Nebula from a system:

# /srv/salt/nebula/remove.sls

nebula_purge:
  module.run:
    - nebula.purge:
      - remove_package: True

Run with:

salt web01 state.apply nebula.remove

Conditional Deployment Based on Pillar

Only deploy if the minion has Nebula configuration:

# /srv/salt/nebula/init.sls

{% if salt['pillar.get']('nebula:hosts:' ~ grains['id']) %}

# ... full deployment states ...

{% else %}

nebula_not_configured:
  test.show_notification:
    - text: "No Nebula configuration found for {{ grains['id'] }} in pillar"

{% endif %}