AlmaLinux

Install Puppet 8 Server and Agent on Rocky / AlmaLinux 10

Puppet follows a server-agent architecture where you define infrastructure state in declarative manifests, and every managed node enforces that state automatically. Instead of SSHing into dozens of machines to update a config file, you write it once, push it to the Puppet Server, and let every agent pull the change on its next check-in. If someone manually edits a managed file or stops a service, Puppet catches the drift and corrects it within 30 minutes.

Original content from computingforgeeks.com - post 31726

This guide walks through installing Puppet 8 Server and Agent on Rocky Linux 10 (also works on AlmaLinux 10 and RHEL 10). Puppet doesn’t ship official EL-10 packages yet, but the EL-9 packages install and run correctly with one small workaround for Java. We cover the full setup: repository installation, server and agent configuration, certificate signing, writing manifests, deploying a real Nginx configuration to a managed node, and testing drift remediation. If you need the same setup on Debian or Ubuntu, check our guide on setting up Puppet Server and Agent on Ubuntu 24.04.

Last verified: March 2026 | Tested on Rocky Linux 10.1 with Puppet Server 8.7.0, Puppet Agent 8.10.0, OpenJDK 21.0.10

Prerequisites

You need two Rocky Linux 10 or AlmaLinux 10 servers. Here are the minimum specs:

  • Puppet Server: 2 CPU cores, 4 GB RAM minimum (Puppet Server runs on the JVM and needs at least 2 GB heap)
  • Puppet Agent: 1 CPU core, 1 GB RAM
  • Root or sudo access on both servers
  • Port 8140/tcp open between the two nodes (Puppet’s communication port)
  • Hostnames configured and resolvable between servers (DNS or /etc/hosts)
  • Tested on: Rocky Linux 10.1, Puppet Server 8.7.0, Puppet Agent 8.10.0, OpenJDK 21.0.10

Here is the lab setup used throughout this guide:

RoleHostnameIP AddressOS
Puppet Serverpuppet-server.example.com10.0.1.10Rocky Linux 10
Puppet Agentpuppet-agent.example.com10.0.1.11Rocky Linux 10

Replace the IP addresses with your own. The steps are identical for AlmaLinux 10 and RHEL 10.

Step 1: Configure Hostnames and DNS Resolution

Puppet relies heavily on hostnames for certificate generation and agent-to-server communication. Both servers must resolve each other by FQDN before you install anything.

On the Puppet Server node, set the hostname:

sudo hostnamectl set-hostname puppet-server.example.com

On the Puppet Agent node:

sudo hostnamectl set-hostname puppet-agent.example.com

If you do not have a DNS server handling resolution between the nodes, add entries to /etc/hosts on both servers:

sudo vi /etc/hosts

Add these lines (replace with your actual IPs):

10.0.1.10  puppet-server.example.com  puppet-server
10.0.1.11  puppet-agent.example.com   puppet-agent

Verify hostname resolution from both servers:

hostname -f

The output should show the full FQDN you just set:

puppet-server.example.com

Test connectivity by pinging each server from the other:

ping -c 3 puppet-server.example.com
ping -c 3 puppet-agent.example.com

Step 2: Install the Puppet 8 Repository

Puppet packages are not in the default Rocky Linux or AlmaLinux repositories. As of March 2026, Puppet Labs has not released official EL-10 packages, but the EL-9 packages install and run correctly on Rocky Linux 10. Install the official Puppet 8 release repository on both the server and the agent:

sudo dnf install -y https://yum.puppet.com/puppet8-release-el-9.noarch.rpm

Confirm the repository is available:

sudo dnf repolist | grep puppet

You should see the puppet8 repository listed:

puppet8                     Puppet 8 Repository el 9 - x86_64

Step 3: Install Puppet Agent on Both Servers

Install the puppet-agent package on both servers. This provides the Puppet binary, Facter, and all required Ruby dependencies. The EL-9 agent package installs cleanly on Rocky Linux 10 with no modifications:

sudo dnf install -y puppet-agent

After installation, load the Puppet binaries into your shell PATH:

source /etc/profile.d/puppet-agent.sh

Verify the installed version:

puppet --version

You should see:

8.10.0

Step 4: Install Puppet Server

On the Puppet Server node only, install the puppetserver package. This is where the EL-10 workaround comes in. The puppetserver RPM depends on java-17-openjdk-headless, which is not available on Rocky Linux 10 (EL-10 ships Java 21 and 25 instead). The fix is straightforward: install Java 21, then install the puppetserver RPM while skipping the Java 17 dependency check.

First, install Java 21:

sudo dnf install -y java-21-openjdk-headless

Confirm Java 21 is installed:

java -version

You should see output similar to:

openjdk version "21.0.10" 2026-01-20 LTS
OpenJDK Runtime Environment (Red_Hat-21.0.10.0.7-1) (build 21.0.10+7-LTS)
OpenJDK 64-Bit Server VM (Red_Hat-21.0.10.0.7-1) (build 21.0.10+7-LTS, mixed mode, sharing)

Now download the puppetserver RPM and install it with --nodeps to bypass the Java 17 dependency:

cd /tmp
sudo dnf download puppetserver

Install the downloaded RPM:

sudo rpm -ivh --nodeps /tmp/puppetserver-*.noarch.rpm
rm -f /tmp/puppetserver-*.noarch.rpm

The --nodeps flag is safe here because we already installed a compatible Java version. Puppet Server 8.7.0 works correctly with OpenJDK 21. Verify both components are installed:

source /etc/profile.d/puppet-agent.sh
puppet --version
puppetserver --version

Expected output:

8.10.0
puppetserver version: 8.7.0

Step 5: Configure Puppet Server

Before starting the Puppet Server service, configure the main settings in puppet.conf. This file controls how both the server and agent behave.

Set the server hostname and certificate name:

sudo puppet config set server puppet-server.example.com --section main
sudo puppet config set certname puppet-server.example.com --section main

Set DNS alternative names for the server certificate. This allows agents to connect using any of these names:

sudo puppet config set dns_alt_names 'puppet-server.example.com,puppet-server,puppet' --section server

Verify the configuration:

cat /etc/puppetlabs/puppet/puppet.conf

The relevant sections should look like this:

[main]
server = puppet-server.example.com
certname = puppet-server.example.com

[server]
vardir = /opt/puppetlabs/server/data/puppetserver
logdir = /var/log/puppetlabs/puppetserver
rundir = /var/run/puppetlabs/puppetserver
pidfile = /var/run/puppetlabs/puppetserver/puppetserver.pid
codedir = /etc/puppetlabs/code
dns_alt_names = puppet-server.example.com,puppet-server,puppet

Adjust JVM Memory Allocation

Puppet Server allocates 2 GB of heap memory by default. For a small deployment (under 10 agents), you can reduce this to 1 GB. For larger environments, keep the default or increase it. The configuration is in /etc/sysconfig/puppetserver:

sudo vi /etc/sysconfig/puppetserver

Find the JAVA_ARGS line and adjust -Xms and -Xmx as needed:

JAVA_ARGS="-Xms2g -Xmx2g -Djruby.logger.class=com.puppetlabs.jruby_utils.jruby.Slf4jLogger"

The JAVA_BIN line should already point to /usr/bin/java, which is Java 21 on Rocky 10. No changes needed there.

Step 6: Start Puppet Server and Configure Firewall

Enable and start the Puppet Server service. The first start takes 30 to 60 seconds because the JVM needs to initialize and generate the CA certificate:

sudo systemctl enable --now puppetserver

Check the service status:

sudo systemctl status puppetserver

You should see active (running):

● puppetserver.service - puppetserver Service
     Loaded: loaded (/usr/lib/systemd/system/puppetserver.service; enabled; preset: disabled)
     Active: active (running)
   Main PID: 2821 (java)
     Memory: 1.2G

Verify Puppet Server is listening on port 8140:

ss -tlnp | grep 8140

Expected output:

LISTEN 0      50          *:8140          *:*    users:(("java",pid=2821,fd=7))

Open port 8140 in the firewall so agents can connect:

sudo dnf install -y firewalld
sudo systemctl enable --now firewalld
sudo firewall-cmd --add-port=8140/tcp --permanent
sudo firewall-cmd --reload

Verify the port is open:

sudo firewall-cmd --list-ports

You should see 8140/tcp listed.

Step 7: Configure and Connect the Puppet Agent

On the Puppet Agent node, configure it to point to the Puppet Server:

source /etc/profile.d/puppet-agent.sh
sudo puppet config set server puppet-server.example.com --section main
sudo puppet config set certname puppet-agent.example.com --section main

Run the agent for the first time. This generates a certificate signing request (CSR) and sends it to the server:

sudo puppet agent --test

The first run will pause because the certificate has not been signed yet. You will see output like this:

Info: Creating a new RSA SSL key for puppet-agent.example.com
Info: csr_attributes file loading from /etc/puppetlabs/puppet/csr_attributes.yaml
Info: Creating a new SSL certificate request for puppet-agent.example.com
Info: Certificate Request fingerprint (SHA256): DC:22:13:62:64:1A:D2:57:D6:74:03:99:D1:CF:2C:F9:...

Step 8: Sign the Agent Certificate on the Server

Back on the Puppet Server, list pending certificate requests:

sudo puppetserver ca list

You should see the agent’s request:

Requested Certificates:
    puppet-agent.example.com       (SHA256)  DC:22:13:62:64:1A:D2:57:D6:74:03:99:D1:CF:2C:F9:...

Sign it:

sudo puppetserver ca sign --certname puppet-agent.example.com

The output confirms the signing:

Successfully signed certificate request for puppet-agent.example.com

Now run the agent again on the Agent node. This time it downloads the signed certificate and fetches the catalog:

sudo puppet agent --test

A successful first catalog run looks like this:

Info: Downloaded certificate for puppet-agent.example.com from https://puppet-server.example.com:8140/puppet-ca/v1
Info: Using environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Notice: Requesting catalog from puppet-server.example.com:8140 (10.0.1.10)
Notice: Catalog compiled by puppet-server.example.com
Info: Applying configuration version '1774358584'
Notice: Applied catalog in 0.01 seconds

The agent and server are now connected. Verify all signed certificates on the server:

sudo puppetserver ca list --all

You should see both certificates listed:

Signed Certificates:
    puppet-server.example.com       (SHA256)  D3:2C:3F:54:...  alt names: ["DNS:puppet-server.example.com", "DNS:puppet-server", "DNS:puppet"]
    puppet-agent.example.com        (SHA256)  82:BF:3F:91:...

Step 9: Write a Real Puppet Manifest (Nginx Deployment)

A Puppet manifest without a real workload is just a hello world. Let us deploy a complete Nginx setup to the agent node, including a custom server block, a managed index page, firewall rules, a system user, and NTP configuration. This tests package management, file management, service management, user management, and dependency ordering all in one manifest.

On the Puppet Server, create the site manifest:

sudo vi /etc/puppetlabs/code/environments/production/manifests/site.pp

Add the following manifest:

node 'puppet-agent.example.com' {
  # Install and manage Nginx
  package { 'nginx':
    ensure => installed,
  }

  # Deploy a custom index page
  file { '/usr/share/nginx/html/index.html':
    ensure  => file,
    content => "<html><body><h1>Managed by Puppet</h1>
               <p>Deployed by Puppet Server 8.7.0 on Rocky Linux 10</p>
               </body></html>\n",
    require => Package['nginx'],
  }

  # Custom Nginx server block on port 8080
  file { '/etc/nginx/conf.d/custom.conf':
    ensure  => file,
    content => "server {
    listen 8080;
    server_name puppet-agent.example.com;

    location / {
        root /usr/share/nginx/html;
        index index.html;
    }

    location /status {
        stub_status on;
        allow 10.0.1.0/24;
        deny all;
    }
}\n",
    require => Package['nginx'],
    notify  => Service['nginx'],
  }

  # Start and enable Nginx
  service { 'nginx':
    ensure  => running,
    enable  => true,
    require => Package['nginx'],
  }

  # Install and enable firewalld
  package { 'firewalld':
    ensure => installed,
  }

  service { 'firewalld':
    ensure  => running,
    enable  => true,
    require => Package['firewalld'],
  }

  # Create a deployment user
  user { 'deploy':
    ensure     => present,
    shell      => '/bin/bash',
    managehome => true,
    comment    => 'Deployment user managed by Puppet',
  }

  # Ensure NTP is configured
  package { 'chrony':
    ensure => installed,
  }

  service { 'chronyd':
    ensure  => running,
    enable  => true,
    require => Package['chrony'],
  }
}

This manifest installs Nginx with a custom server block on port 8080, deploys a managed index page, sets up firewalld, creates a deploy user, and ensures chrony (NTP) is running. The require and notify parameters enforce proper ordering, so Nginx restarts automatically when its config file changes.

Validate the manifest syntax before applying it:

sudo puppet parser validate /etc/puppetlabs/code/environments/production/manifests/site.pp

No output means the syntax is valid.

Step 10: Apply the Manifest on the Agent

On the Agent node, trigger a Puppet run to apply the manifest:

sudo puppet agent --test

Puppet installs all packages, creates files, starts services, and creates the user. The output shows each change:

Notice: /Stage[main]/Main/Node[puppet-agent.example.com]/Package[nginx]/ensure: created
Notice: /Stage[main]/Main/Node[puppet-agent.example.com]/File[/usr/share/nginx/html/index.html]/ensure: defined content as '{sha256}a45de93...'
Notice: /Stage[main]/Main/Node[puppet-agent.example.com]/File[/etc/nginx/conf.d/custom.conf]/ensure: defined content as '{sha256}e10d0cc...'
Notice: /Stage[main]/Main/Node[puppet-agent.example.com]/Service[nginx]/ensure: ensure changed 'stopped' to 'running'
Notice: /Stage[main]/Main/Node[puppet-agent.example.com]/Package[firewalld]/ensure: created
Notice: /Stage[main]/Main/Node[puppet-agent.example.com]/Service[firewalld]/ensure: ensure changed 'stopped' to 'running'
Notice: /Stage[main]/Main/Node[puppet-agent.example.com]/User[deploy]/ensure: created
Notice: Applied catalog in 402.88 seconds

Verify the deployment worked by testing the Nginx custom page:

curl http://localhost:8080/

You should see the Puppet-managed page content. Check the deploy user was created:

id deploy

The output confirms the user exists:

uid=1001(deploy) gid=1001(deploy) groups=1001(deploy)

Open the Nginx and Puppet ports in firewalld on the agent:

sudo firewall-cmd --add-port=8080/tcp --permanent
sudo firewall-cmd --add-port=8140/tcp --permanent
sudo firewall-cmd --add-service=http --permanent
sudo firewall-cmd --reload

Step 11: Test Drift Remediation

One of Puppet’s strongest features is automatic drift correction. If someone manually changes a managed file, Puppet reverts it on the next run. This is critical in production where unauthorized changes can break systems silently.

Simulate drift by manually overwriting the managed index page on the agent:

echo "HACKED! This was manually changed." | sudo tee /usr/share/nginx/html/index.html

Confirm the file was changed:

curl http://localhost:8080/

You will see the modified content:

HACKED! This was manually changed.

Now run the Puppet agent:

sudo puppet agent --test

Puppet detects the content change and corrects it:

Notice: /Stage[main]/Main/Node[puppet-agent.example.com]/File[/usr/share/nginx/html/index.html]/content:
content changed '{sha256}80fd72f3...' to '{sha256}a45de939...' (corrective)
Notice: Applied catalog in 0.11 seconds

Verify the original content is restored:

curl http://localhost:8080/

The Puppet-managed content is back. In production, the Puppet agent runs as a background service every 30 minutes by default, so drift gets corrected automatically without manual intervention.

Step 12: Enable the Puppet Agent Service

By default, the Puppet agent does not run as a background service after installation. Enable it so the agent checks in with the server every 30 minutes:

sudo puppet resource service puppet ensure=running enable=true

The output confirms the service is running:

service { 'puppet':
  ensure   => 'running',
  enable   => 'true',
  provider => 'systemd',
}

You can adjust the check-in interval by setting runinterval in puppet.conf. For example, to check every 15 minutes:

sudo puppet config set runinterval 900 --section main

Rocky Linux 10 vs Rocky Linux 9 Differences

For teams running mixed environments, here are the key differences when running Puppet 8 on Rocky Linux 10 compared to Rocky Linux 9:

ItemRocky Linux 9Rocky Linux 10
Puppet repo packagepuppet8-release-el-9.noarch.rpmpuppet8-release-el-9.noarch.rpm (same)
puppet-agent installdnf install puppet-agentdnf install puppet-agent (works directly)
puppetserver installdnf install puppetserverrpm -ivh --nodeps (Java 17 dep missing)
Java versionOpenJDK 17OpenJDK 21
Kernel5.14.x6.12.x
SELinuxEnforcing (no issues)Enforcing (no issues)
FirewalldInstalled by defaultMay need manual install

Once installed, Puppet 8 behaves identically on both OS versions. All manifests, modules, and Hiera data work without modification.

Troubleshooting Common Issues

Error: “nothing provides java-17-openjdk-headless needed by puppetserver”

This is the expected error when trying to install puppetserver via dnf install on Rocky Linux 10. EL-10 does not include Java 17 in its repositories. The fix is to install Java 21 first, then use rpm -ivh --nodeps to install the puppetserver RPM as described in Step 4.

Error: “Connection refused” when running puppet agent –test

This means the Puppet Server is not running or port 8140 is blocked by the firewall. Check the service status with systemctl status puppetserver and verify port 8140 is open with sudo firewall-cmd --list-ports. Also confirm the agent can resolve the server hostname with ping puppet-server.example.com.

Error: “certificate verify failed” on the agent

If you rebuilt the Puppet Server and the agent still has old certificates, clean the agent’s SSL directory and re-register:

sudo puppet ssl clean
sudo puppet agent --test

Then sign the new certificate request on the server.

Puppet Server takes too long to start

The first startup can take 60 to 120 seconds because the JVM compiles JRuby code. Subsequent starts are faster. If it consistently fails to start, check /var/log/puppetlabs/puppetserver/puppetserver.log for Java heap errors. Increase the heap size in /etc/sysconfig/puppetserver if you see OutOfMemoryError.

Puppet agent reports “Could not request certificate”

This usually means the agent cannot reach port 8140 on the server. Verify network connectivity with nc -zv puppet-server.example.com 8140. On the server, ensure the firewall allows the connection and that ss -tlnp | grep 8140 shows the port listening.

What port does Puppet use?

Puppet Server listens on TCP port 8140 by default. All agent-to-server communication (catalog requests, certificate signing, file serving) happens over this single port using HTTPS with mutual TLS authentication. You only need to open port 8140/tcp on the server’s firewall.

Wrapping Up

You now have a working Puppet 8 infrastructure on Rocky Linux 10 with a server managing an agent node that runs Nginx, firewalld, chrony, and a managed user account. The EL-9 packages work reliably on EL-10 with the Java 21 workaround, and once installed, everything behaves exactly as it would on Rocky 9. For production use, consider setting up Ansible alongside Puppet for ad-hoc tasks, adding more agent nodes, organizing your manifests into Puppet modules, and configuring Hiera for data separation.

Related Articles

AlmaLinux Install MariaDB 12.0 on Rocky Linux 10 / AlmaLinux 10 Openstack Upload RHEL 10/9/8 Qcow2 Image to OpenStack glance AlmaLinux Install and Use aaPanel on Rocky Linux 9 | AlmaLinux 9 AlmaLinux Setup Pydio Cells on Rocky Linux 9 | AlmaLinux 9

Leave a Comment

Press ESC to close