
Puppet Client-Server Configuration: A Comprehensive DevOps Guide
When managing infrastructure at scale, configuration drift becomes your worst enemy. Puppet solves this by enforcing desired state across hundreds of servers from a single master node. In this definitive tutorial, I'll walk you through setting up a production-grade Puppet 6 client-server architecture on CentOS 8 - exactly as I've implemented it for enterprise environments handling 500+ nodes.
Puppet's client-server model (unlike agentless tools like Ansible) provides continuous enforcement of configurations through a persistent agent that checks in with the master every 30 minutes by default. This makes it ideal for compliance-heavy environments where you need to guarantee configuration consistency across your fleet.
Prerequisites
- Two or more CentOS 8 servers (minimum 2GB RAM for master, 1GB for agents)
- Root or sudo privileges on all nodes
- Static IP addresses for all servers
- Basic familiarity with Linux system administration
- Network connectivity between nodes (TCP port 8140 open)
For this guide, we'll use:
- Master server:
master.hakase-labs.io(10.5.5.21) - Agent server:
agent01.hakase-labs.io(10.5.5.22)
Step 1: Puppet Pre-Installation
Before installing Puppet, we need to prepare our environment. This foundation work prevents 80% of the issues I see in production Puppet deployments.
1.1 Configure Hostnames and FQDN
Puppet relies heavily on proper DNS resolution. Misconfigured hostnames are the #1 cause of certificate signing failures.
# On master server
hostnamectl set-hostname master
# On agent server
hostnamectl set-hostname agent01
Edit /etc/hosts on both servers to include both nodes:
vim /etc/hosts
10.5.5.21 master.hakase-labs.io master
10.5.5.22 agent01.hakase-labs.io agent01
Verify the configuration:
hostname
hostname -f
Expected output on master:
master
master.hakase-labs.io
Restart the hostname service to apply changes:
systemctl restart systemd-hostnamed
1.2 Configure NTP with Chrony
Time synchronization is critical for Puppet's certificate authority. Even a 5-minute time difference will break certificate validation. I recommend using chrony over ntpd as it handles network interruptions better.
Install chrony on both servers:
dnf install chrony -y
Edit the configuration file:
vim /etc/chrony.conf
Replace the default pool with country-specific NTP servers (find yours at pool.ntp.org):
server 0.us.pool.ntp.org iburst
server 1.us.pool.ntp.org iburst
server 2.us.pool.ntp.org iburst
server 3.us.pool.ntp.org iburst
Start and enable the service:
systemctl start chronyd
systemctl enable chronyd
Verify synchronization:
chronyc tracking
chronyc sources -v
Look for "Leap status: Normal" and "Source state: Current" in the output.
1.3 Add Puppet Repository
Puppet isn't included in CentOS 8's default repositories. We'll add the official Puppet 6 repository:
sudo rpm -Uvh https://yum.puppet.com/puppet6-release-el-8.noarch.rpm
Verify the repository was added:
dnf repolist | grep puppet
Expected output:
puppet6 Puppet 6 Repository el 8 - x86_64
1.4 Disable SELinux
While Puppet can work with SELinux in enforcing mode, I recommend starting with it disabled to eliminate one variable during initial setup. You can re-enable it later after verifying your configuration works.
Edit the SELinux configuration:
vim /etc/sysconfig/selinux
Change the line to:
SELINUX=disabled
Reboot the server:
sudo reboot
After reboot, verify SELinux is disabled:
sestatus
Expected output:
SELinux status: disabled
Step 2: Install and Configure Puppet Server
The Puppet master (now called "Puppet Server" in version 6) is the central management node that compiles and serves configurations to agents.
2.1 Install Puppet Server
Install the puppetserver package:
sudo dnf install puppetserver -y
2.2 Configure Memory Allocation
The puppetserver service runs on the JVM, which needs proper memory allocation. The default configuration assumes 2GB RAM, which is often too much for lab environments.
Edit the service configuration:
vim /etc/sysconfig/puppetserver
Adjust the memory settings based on your available RAM:
- For 2GB RAM:
JAVA_ARGS="-Xms1g -Xmx1g" - For 4GB RAM:
JAVA_ARGS="-Xms2g -Xmx2g" - For 8GB+ RAM:
JAVA_ARGS="-Xms4g -Xmx4g"
Example for a 2GB server:
JAVA_ARGS="-Xms1g -Xmx1g -Djruby.logger.class=com.puppetlabs.jruby_utils.jruby.Slf4jLogger"
2.3 Configure Puppet Server Settings
Edit the main Puppet configuration file:
cd /etc/puppetlabs/puppet
vim puppet.conf
Add these configurations under the appropriate sections:
[master]
dns_alt_names = master.hakase-labs.io,puppet
[main]
certname = master.hakase-labs.io
server = master.hakase-labs.io
environment = production
runinterval = 1h
Key settings explained:
dns_alt_names: Additional names agents might use to connect to the mastercertname: The name that will appear in certificatesserver: The FQDN agents should use to connectruninterval: How often agents check in (default is 30m)
2.4 Start Puppet Server
Enable and start the service:
systemctl enable puppetserver
systemctl start puppetserver
Verify it's running:
systemctl status puppetserver
Check the listening port:
ss -tulnp | grep 8140
Expected output:
tcp LISTEN 0 100 *:8140 *:* users:(("java",pid=1234,fd=42))
2.5 Configure Firewall
Puppet server listens on TCP port 8140. Add this to firewalld:
firewall-cmd --add-port=8140/tcp --permanent
firewall-cmd --reload
Verify the rule was added:
firewall-cmd --list-ports
Step 3: Install and Configure Puppet Agent
Now we'll configure our first agent node to connect to the master.
3.1 Install Puppet Agent
On the agent server, install the puppet-agent package:
sudo dnf install puppet-agent -y
3.2 Configure Puppet Agent
Edit the agent configuration:
cd /etc/puppetlabs/puppet
vim puppet.conf
Add these settings:
[main]
certname = agent01.hakase-labs.io
server = master.hakase-labs.io
environment = production
runinterval = 1h
3.3 Start Puppet Agent
Enable and start the agent service:
systemctl enable puppet
systemctl start puppet
Verify it's running:
systemctl status puppet
3.4 Sign Agent Certificate on Master
Puppet uses SSL certificates for authentication. The agent generates a certificate request that must be signed by the master.
On the master server, list pending certificate requests:
puppetserver ca list
Expected output:
Requested Certificates:
agent01.hakase-labs.io (SHA256) 12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF
Sign the certificate:
puppetserver ca sign --certname agent01.hakase-labs.io
Verify the certificate was signed:
puppetserver ca list --all
Step 4: Verify Puppet Agent Configuration
Now let's verify the agent can successfully communicate with the master.
4.1 Test Agent Connection
On the agent server, run a manual Puppet run:
puppet agent -t
Expected output:
Info: Using configured environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Retrieving locales
Info: Caching catalog for agent01.hakase-labs.io
Info: Applying configuration version '1234567890'
Notice: Applied catalog in 0.01 seconds
If you see errors about certificate verification, run:
puppet agent --test --waitforcert 60
4.2 Check Reports on Master
On the master server, check the last run report for the agent:
puppetserver ca list --all
puppet node find agent01.hakase-labs.io
View the report:
puppet report list --node agent01.hakase-labs.io
puppet report show <report-hash>
Step 5: Create Your First Puppet Manifest
Now let's create a simple manifest to manage a file on our agent node.
5.1 Create Site Manifest
On the master server, create the main manifest directory structure:
mkdir -p /etc/puppetlabs/code/environments/production/manifests
cd /etc/puppetlabs/code/environments/production/manifests
Create a site manifest file:
vim site.pp
Add this content to manage the /etc/motd file:
node 'agent01.hakase-labs.io' {
file { '/etc/motd':
ensure => file,
content => "Welcome to ${fqdn}\nManaged by Puppet\n",
}
}
5.2 Apply the Manifest
On the agent server, trigger a Puppet run:
puppet agent -t
Verify the file was created:
cat /etc/motd
Expected output:
Welcome to agent01.hakase-labs.io
Managed by Puppet
Common Pitfalls and Troubleshooting
Here are the most common issues I encounter when setting up Puppet, along with their solutions:
Certificate Issues
- Problem: "Could not request certificate: getaddrinfo: Name or service not known"
- Solution: Verify DNS resolution works in both directions. Check
/etc/hostsandhostname -foutput.
- Solution: Verify DNS resolution works in both directions. Check
- Problem: "Server hostname 'master.hakase-labs.io' did not match server certificate"
- Solution: Ensure the master's
certnameinpuppet.confmatches the certificate. Runpuppetserver ca list --allto verify.
- Solution: Ensure the master's
Time Synchronization Issues
- Problem: "SSL_connect returned=1 errno=0 state=error: certificate verify failed"
- Solution: Check time synchronization with
timedatectlandchronyc tracking. Time differences >300 seconds will break SSL.
- Solution: Check time synchronization with
Firewall Issues
- Problem: "Connection refused" when agent tries to connect
- Solution: Verify port 8140 is open on the master:
firewall-cmd --list-ports. Check connectivity withtelnet master.hakase-labs.io 8140from the agent.
- Solution: Verify port 8140 is open on the master:
Memory Issues
- Problem: Puppet server fails to start with "Java heap space" errors
- Solution: Reduce memory allocation in
/etc/sysconfig/puppetserver. For 2GB RAM, use-Xms1g -Xmx1g.
- Solution: Reduce memory allocation in
How to Verify Your Puppet Setup
Use these commands to verify each component of your Puppet infrastructure:
| Component | Verification Command | Expected Output |
|---|---|---|
| Master Service | systemctl status puppetserver |
Active: active (running) |
| Agent Service | systemctl status puppet |
Active: active (running) |
| Certificate Status | puppetserver ca list --all |
Signed certificates for all agents |
| Agent Connection | puppet agent -t |
No errors, catalog applied successfully |
| File Sync | ls -l /etc/puppetlabs/code/environments/ |
production environment directory exists |
| Port Access | telnet master.hakase-labs.io 8140 |
Connected successfully |
Key Takeaways
- DNS is critical: Puppet relies on proper hostname resolution. Always verify
hostname -fand/etc/hostsbefore installation. - Time synchronization matters: Even small time differences will break SSL certificate validation. Use chrony for reliable NTP.
- Memory allocation is important: The puppetserver service needs proper JVM memory settings. Start with 1GB for lab environments.
- Certificate management is manual: Remember to sign agent certificates on the master with
puppetserver ca sign. - Start simple: Begin with basic file resources before moving to complex modules. Verify each step before adding complexity.
FAQ
How do I add more agents to my Puppet master?
For each new agent:
- Install puppet-agent package
- Configure
/etc/puppetlabs/puppet/puppet.confwith the master's FQDN - Start the puppet service
- On master:
puppetserver ca sign --certname <agent-fqdn> - On agent:
puppet agent -t
What's the difference between puppet apply and puppet agent?
puppet apply runs manifests directly on the local system without a master, while puppet agent connects to a master to retrieve and apply configurations. Use apply for testing manifests and agent for production environments.
How do I update Puppet to a newer version?
On CentOS 8:
# Update repository
sudo rpm -Uvh https://yum.puppet.com/puppet7-release-el-8.noarch.rpm
# Update packages
sudo dnf update puppetserver puppet-agent
# Restart services
sudo systemctl restart puppetserver
sudo systemctl restart puppet
How can I check when an agent last checked in?
On the master server:
puppet node find <agent-fqdn>
Look for the "last_run" timestamp in the output. For more detailed reporting:
puppet report list --node <agent-fqdn>
What's the best way to organize Puppet code?
I recommend this structure for production environments:
/etc/puppetlabs/code/environments/
└── production
├── manifests/ # Main manifests
│ └── site.pp # Node definitions
├── modules/ # Custom modules
│ ├── apache/
│ ├── mysql/
│ └── ...
└── hieradata/ # Hiera data
├── common.yaml
└── nodes/
└── agent01.yaml
This setup allows for environment-specific configurations and clean separation of code and data.
Disclosure: some links above are affiliate links — if you buy through them I may earn a small commission at no extra cost to you. Thanks for supporting the channel!