Skip to content

Instantly share code, notes, and snippets.

@ewired
Last active June 24, 2025 23:02
Show Gist options
  • Save ewired/2e1c046d41fd3431abc153712b174a93 to your computer and use it in GitHub Desktop.
Save ewired/2e1c046d41fd3431abc153712b174a93 to your computer and use it in GitHub Desktop.
Quadlet + git quick start and post-receive hook

Quick start for a Git-based Quadlet container host

This guide will help you set up a git repository to manage your Podman Quadlet container configurations. The included post-receive hook will automatically deploy your container configurations when you push changes.

⚠️ Important Warning

This setup will delete any existing Quadlet units in your ~/.config/containers/systemd directory as it is designed to manage your entire Quadlet configuration through the git repository.

Step 1: Create the Bare Git Repository

Your container host will need a Linux distribution with systemd, Podman and Git installed. On your target host, create a bare git repository:

# Make sure to the same values as the post-receive hook does
USERNAME=$(whoami)
HOSTNAME=$(hostname)
REPO_NAME="${USERNAME}-at-${HOSTNAME}"

# Create the bare repository
git init --bare "$HOME/${REPO_NAME}.git"

# Copy the post-receive hook
curl "https://gist.githubusercontent.com/ewired/2e1c046d41fd3431abc153712b174a93/raw/a424100f04f8828d0616e550c324b4935cc82f99/post-receive.sample" > "$HOME/${REPO_NAME}.git/hooks/post-receive"

# Make the hook executable
chmod +x "$HOME/${REPO_NAME}.git/hooks/post-receive"

Step 2: Enable User Services on Boot

Ensure your user's systemd services start automatically on boot:

# Enable lingering for your user account
sudo loginctl enable-linger $USER

# Verify lingering is enabled
loginctl show-user $USER | grep Linger

Step 3: Clone the Repository

From your development machine, clone the repository via SSH:

# Replace with your actual username and hostname/IP
git clone ssh://username@hostname/home/username/username-at-hostname.git username-at-hostname
cd username-at-hostname

Step 4: Create Initial Directory Structure

Set up the basic directory structure:

# Create the containers directory
mkdir -p containers

# Initialize the repository with a README
echo "# My host" > README.md

git add README.md
git commit -m "Initial commit with README"

Step 5: Create your units

The manual is great: https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html

# Create a simple nginx container unit
cat > containers/demo-nginx.container << 'EOF'
[Unit]
Description=Demo Nginx Web Server
Wants=network-online.target
After=network-online.target

[Container]
Image=docker.io/library/nginx:alpine
AutoUpdate=registry
PublishPort=8080:80
# Important: Volumes must be referenced by the full filename with .volume if managed by Quadlet
Volume=demo-nginx-website.volume:/usr/share/nginx/html

[Service]
Restart=on-failure
TimeoutStopSec=30

[Install]
# Important: To start on boot, use this
WantedBy=default.target
EOF

# Create a volume for the nginx container
cat > containers/demo-nginx-website.volume << 'EOF'
[Unit]
Description=Demo Nginx Website Volume
EOF

Step 6: Commit and Push the Configuration

# Add the new container configuration
git add containers/

# Commit the changes
git commit -m "Add demo nginx container configuration"

# Push to deploy (this will trigger the post-receive hook)
git push origin main

Step 7: Start the New Container

After pushing, the post-receive hook will have updated your Quadlet configurations. Now start the container:

# Reload systemd to pick up new units (done automatically by hook, but shown for reference)
systemctl --user daemon-reload

# Start the demo container
systemctl --user start demo-nginx.service

# Check the status
systemctl --user status demo-nginx.service

# Verify the container is running
podman ps

# Test the nginx server (if you created the nginx example)
curl http://localhost:8080

Important Notes About Quadlet Units

  • Auto-enabling: Unlike regular systemd services, Quadlet units with WantedBy=default.target are automatically enabled when present. You don't need to run systemctl --user enable.
  • File extensions: Use .container for containers, .volume for volumes, and .pod for pods.
  • Dependencies: Use After= and Wants= to define service dependencies.
  • Updates: Set AutoUpdate=registry to pull newer container images with podman auto-update on the host.

Managing Your Configuration

Adding New Containers

  1. Create new .container, .volume, or .pod files in the containers/ directory
  2. Commit and push your changes
  3. The containers will be automatically available after the push

Updating Existing Containers

  1. Modify the unit files in the containers/ directory
  2. Commit and push your changes
  3. Restart affected services: systemctl --user restart service-name.service

Removing Containers

  1. Delete the unit files from the containers/ directory
  2. Commit and push your changes
  3. Stop and disable the services manually if needed:
    systemctl --user stop service-name.service
    systemctl --user reset-failed service-name.service

Troubleshooting

Check systemd status

# View all user services
# These units don't show up under list-units because they're generated on-demand by Quadlet.
systemctl --user list-unit-files

# Check failed services
systemctl --user --failed

# View logs while restarting services to troubleshoot
journalctl -xef

Verify Quadlet files

# Check if Quadlet files are properly linked
ls -la ~/.config/containers/systemd/

# Validate Quadlet syntax and show the real systemd units
/usr/libexec/podman/quadlet --dryrun --user

Repository status

# Check the deployed files
ls -la ~/${USERNAME}-at-${HOSTNAME}/containers/

# Verify git status
cd ~/${USERNAME}-at-${HOSTNAME}
git status
git log --oneline -5
#!/bin/bash
set -e
# Get current username and hostname
USERNAME=$(whoami)
HOSTNAME=$(hostname)
DIR_NAME="${USERNAME}-at-${HOSTNAME}"
# Define target directories
WORK_TREE="$HOME/${DIR_NAME}"
QUADLET_DIR="$HOME/.config/containers/systemd"
# Make sure target directories exist
mkdir -p "$WORK_TREE" "$QUADLET_DIR"
# Checkout the latest code to the work tree
git --work-tree="$WORK_TREE" --git-dir="$HOME/${DIR_NAME}.git" checkout -f main
# Create symbolic links for Quadlet files
echo "Updating Quadlet container units..."
find "$QUADLET_DIR" -type l -delete # Remove old symlinks
ln -sf "$WORK_TREE/containers/"* "$QUADLET_DIR/" 2>/dev/null || true
# Reload systemd to recognize changes
echo "Reloading systemd daemon..."
systemctl --user daemon-reload
echo "Deployment complete!"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment