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.
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.
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"
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
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
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"
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
# 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
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
- Auto-enabling: Unlike regular systemd services, Quadlet units with
WantedBy=default.target
are automatically enabled when present. You don't need to runsystemctl --user enable
. - File extensions: Use
.container
for containers,.volume
for volumes, and.pod
for pods. - Dependencies: Use
After=
andWants=
to define service dependencies. - Updates: Set
AutoUpdate=registry
to pull newer container images withpodman auto-update
on the host.
- Create new
.container
,.volume
, or.pod
files in thecontainers/
directory - Commit and push your changes
- The containers will be automatically available after the push
- Modify the unit files in the
containers/
directory - Commit and push your changes
- Restart affected services:
systemctl --user restart service-name.service
- Delete the unit files from the
containers/
directory - Commit and push your changes
- Stop and disable the services manually if needed:
systemctl --user stop service-name.service systemctl --user reset-failed service-name.service
# 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
# 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
# Check the deployed files
ls -la ~/${USERNAME}-at-${HOSTNAME}/containers/
# Verify git status
cd ~/${USERNAME}-at-${HOSTNAME}
git status
git log --oneline -5