Skip to content

iOS, macOS, web, and science. Hacked.

  • Articles
  • About
  • Colophon

Search

Automated Git Commits With Inotify

February 19, 2026 20 min read Coding, Homelab, Proxmox

Automating Git Commits with inotify: Never Forget to Push Again

Build a real-time file watcher that automatically commits and pushes your changes to GitHub


Introduction

As developers and technical writers, we’ve all been there: you spend hours writing documentation, blog posts, or code, only to realize hours (or days) later that you forgot to commit and push your changes. Or worse, you lose work because it was never backed up to a remote repository. If you have a homelab, you are likely working with a Proxmox instance via SSH. In that case, you are working with a Linux kernel, and it’s easy to forget to commit and push your changes.

What if your files could automatically commit and push themselves to GitHub the moment you save them? No manual git add, git commit, git push dance. Just save your file, and seconds later it’s safely on GitHub.

In this guide, I’ll show you how to build an automated Git commit system using Linux’s inotify file monitoring system and systemd services. This setup watches a directory for changes and automatically commits and pushes to GitHub in real-time.

What We’re Building

By the end of this tutorial, you’ll have:

  • ✅ Real-time file monitoring that detects when files change
  • ✅ Automatic Git commits with timestamps
  • ✅ Automatic pushes to GitHub within seconds of saving
  • ✅ Background daemon that runs continuously via systemd
  • ✅ Debouncing logic to prevent commit spam during active editing
  • ✅ Smart exclusions for temporary files and Git metadata
  • ✅ Secret detection that aborts commits if credentials are staged
  • ✅ Concurrency protection via lock files to prevent race conditions
  • ✅ Comprehensive logging via journald for troubleshooting

Use Cases

This automation is perfect for:

  • Blog posts and documentation – Never lose writing progress
  • Configuration files – Automatically version control your dotfiles
  • Notes and knowledge bases – Keep Obsidian vaults or markdown notes backed up
  • Small projects – Continuous backup without thinking about it
  • Learning journals – Document your learning with automatic version history

Not recommended for:

  • Large collaborative projects (too many automatic commits)
  • Code requiring peer review before commits
  • Projects where commit messages need careful crafting

Prerequisites

What You’ll Need

System Requirements:

  • Linux system (Ubuntu, Debian, Proxmox, or similar)
  • Git installed and configured
  • SSH key set up with GitHub (or HTTPS with credential caching)
  • Sudo/root access for installing packages

Knowledge Prerequisites:

  • Basic Git concepts (commit, push, remote)
  • Basic Linux terminal usage
  • Understanding of file permissions and executables

Verify Git Setup

Before we begin, ensure Git is configured:

# Check Git installation
git --version

# Check Git configuration
git config --global user.name
git config --global user.email

# If not set, configure them:
git config --global user.name "Your Name"
git config --global user.email "your-email@example.com"

Verify GitHub Authentication

Test that you can push to GitHub:

# For SSH (recommended)
ssh -T git@github.com

# Should see: "Hi username! You've successfully authenticated..."

If authentication fails, you need to set up SSH keys or HTTPS credentials before proceeding.

Architecture Overview

Let’s understand how this system works:

┌─────────────────┐
│   You save a    │
│   file in the   │
│   watched dir   │
└────────┬────────┘
         │
         ▼
┌─────────────────────┐
│   inotifywait       │
│   detects change    │
│   (modify/create/   │
│    delete/move)     │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│   Debounce timer    │
│   (5 second wait)   │
│   Allows multiple   │
│   saves to batch    │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│   auto-commit.sh    │
│   - git add -u      │
│   - secret check    │
│   - git commit      │
│   - git push        │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│      GitHub         │
│   Changes appear    │
│   in repository     │
└─────────────────────┘

Key Components:

  1. inotify – Linux kernel subsystem that monitors filesystem events
  2. inotifywait – Command-line tool to watch for file changes
  3. auto-commit.sh – Script that performs Git operations
  4. watch-and-commit.sh – Wrapper that monitors files and triggers commits
  5. systemd service – Background daemon that runs the watcher continuously

Step 1: Create Your Git Repository

First, let’s set up the directory you want to auto-commit.

Initialize Git Repository (this tutorial walks through a workflow of maintaining a repo of blog posts)

# Create your directory (or use an existing one)
mkdir -p ~/blogposts
cd ~/blogposts

# Initialize Git
git init

# Rename default branch to 'main'
git branch -m main

# Configure Git for this repository
git config user.name "Your Name"
git config user.email "your-email@example.com"

Create .gitignore

Exclude files you don’t want to commit:

cat > .gitignore << 'EOF'
# macOS
.DS_Store
.AppleDouble
.LSOverride
._*

# Editor backup files
*~
*.swp
*.swo
*.swn
.vscode/
.idea/

# Logs from our automation
*.log

# Temporary files
*.tmp
*.temp

# Automation scripts (keep outside version control)
auto-commit.sh
watch-and-commit.sh

# Security - never auto-commit these
.env
.env.*
*.pem
*.key
*.p12
*.pfx
*.gpg
credentials*
secrets*
id_rsa*
id_ed25519*
*.secret

# Drafts (optional - uncomment if you want to exclude drafts)
# drafts/
# private/
EOF

Create README

cat > README.md << 'EOF'
# My Blog Posts

A collection of technical writing and blog posts, automatically backed up to GitHub.

## Auto-Commit System

This repository uses an automated commit system that watches for file changes and pushes to GitHub automatically.

See [automated-git-commits-with-inotify.md](./automated-git-commits-with-inotify.md) for setup details.
EOF

Create Initial Commit

git add .
git commit -m "Initial commit: Set up repository"

Step 2: Create GitHub Repository

Now we need a remote repository to push to.

  1. Go to GitHub.com and log in
  2. Click the + icon (top right) → New repository
  3. Configure:
    • Repository name: blogposts (or your preferred name)
    • Description: “Automated blog post backups”
    • Visibility: Public or Private
    • DO NOT initialize with README (we already have one)
  4. Click Create repository

Link Local to Remote

# Add GitHub as remote (use SSH - recommended)
git remote add origin git@github.com:your-username/blogposts.git

# Or use HTTPS if you prefer
# git remote add origin https://github.com/your-username/blogposts.git

# Verify remote
git remote -v

# Push initial commit
git push -u origin main

If the push fails due to authentication, ensure your SSH key is added to GitHub or set up credential caching for HTTPS.

Step 3: Install inotify-tools

The inotify-tools package provides inotifywait, which monitors file changes.

# Update package list
sudo apt update

# Install inotify-tools
sudo apt install -y inotify-tools

# Verify installation
inotifywait --version

What is inotify?

inotify is a Linux kernel subsystem that provides file system event monitoring. It’s incredibly efficient because it operates at the kernel level rather than polling for changes. When a file is modified, created, or deleted, inotify immediately notifies interested programs.

★ Insight ─────────────────────────────────────
Unlike polling-based file monitoring (checking every few seconds), inotify uses kernel-level filesystem events, making it extremely efficient. It only triggers when actual changes occur, consuming minimal CPU. This is the same technology used by editors like VS Code to detect external file changes and by build tools like webpack for hot reloading.
─────────────────────────────────────────────────

Step 4: Create Auto-Commit Script

This script performs the actual Git operations.

cat > ~/blogposts/auto-commit.sh << 'EOF'
#!/bin/bash
set -euo pipefail

# Auto-commit script for blogposts
# This script checks for changes and commits them automatically

: "${HOME:?HOME is not set}"
REPO_DIR="$HOME/blogposts"
cd "$REPO_DIR" || exit 1

# Prevent concurrent runs with a lock file
LOCK_FILE="/tmp/blogpost-autocommit.lock"
exec 200>"$LOCK_FILE"
flock -n 200 || { echo "$(date): Another commit in progress, skipping"; exit 0; }

# Check if there are any changes
if [[ -n $(git status --porcelain) ]]; then
    echo "$(date): Changes detected in blogposts"

    # Stage only tracked files (safer than 'git add .' which could commit secrets)
    git add -u

    # Also add new .md and .txt files explicitly (safe allowlist)
    git add -- '*.md' '*.txt' '.gitignore' 2>/dev/null || true

    # Safety check: abort if staged files look like secrets
    if git diff --cached --name-only | grep -qiE '.(env|pem|key|p12|pfx|gpg|secret|credentials)'; then
        echo "$(date): ERROR - Potential secrets detected in staged files. Aborting commit."
        git reset HEAD
        exit 1
    fi

    # Create commit message with timestamp
    COMMIT_MSG="Auto-commit: Update blog posts - $(date '+%Y-%m-%d %H:%M:%S')"

    # Commit changes
    git commit -m "$COMMIT_MSG"

    # Pull any remote changes first to avoid non-fast-forward errors
    git pull --rebase origin main 2>/dev/null || true

    # Push to GitHub (main branch)
    if git push origin main; then
        echo "$(date): Changes committed and pushed successfully"
    else
        echo "$(date): Error pushing to GitHub"
        exit 1
    fi
else
    echo "$(date): No changes detected"
fi
EOF

# Make executable
chmod +x ~/blogposts/auto-commit.sh

Test the Script Manually

# Create a test file
echo "# Test" > ~/blogposts/test.md

# Run the script
~/blogposts/auto-commit.sh

# Check GitHub - the file should appear!

Step 5: Create File Watcher Script

This script uses inotifywait to monitor for changes and trigger commits.

cat > ~/blogposts/watch-and-commit.sh << 'EOF'
#!/bin/bash
set -euo pipefail

# Real-time file watcher for blogposts
# This script watches for file changes and automatically commits them

: "${HOME:?HOME is not set}"
WATCH_DIR="$HOME/blogposts"
SCRIPT_DIR="$HOME/blogposts"

# Colors for output
GREEN='33[0;32m'
YELLOW='33[1;33m'
NC='33[0m' # No Color

echo -e "${GREEN}🔍 Blogpost Auto-Commit Watcher Started${NC}"
echo "Watching: $WATCH_DIR"
echo "Excluding: .git/, logs, temp files"
echo "Press Ctrl+C to stop"
echo "---"

# Track last commit time to debounce rapid changes
LAST_COMMIT_TIME=0
DEBOUNCE_SECONDS=5

inotifywait -m -r -e modify,create,delete,move 
    --format '%w%f' 
    --exclude '(.git|.swp|.log$|auto-commit.log|watcher.log|~$|.DS_Store)' 
    "$WATCH_DIR" |
while IFS= read -r filepath; do
    # Extract filename from full path
    filename=$(basename "$filepath")

    # Skip if it's the auto-commit script itself
    if [[ "$filename" == "auto-commit.sh" ]] || [[ "$filename" == "watch-and-commit.sh" ]]; then
        continue
    fi

    CURRENT_TIME=$(date +%s)
    TIME_DIFF=$((CURRENT_TIME - LAST_COMMIT_TIME))

    if [ $TIME_DIFF -lt $DEBOUNCE_SECONDS ]; then
        echo -e "${YELLOW}⏳ Waiting for more changes (${TIME_DIFF}s ago)...${NC}"
        continue
    fi

    echo -e "${GREEN}📝 Change detected:${NC} $filename"
    echo "⏰ $(date '+%Y-%m-%d %H:%M:%S')"
    echo "Waiting ${DEBOUNCE_SECONDS} seconds for additional changes..."
    sleep $DEBOUNCE_SECONDS

    echo "Running auto-commit..."
    "$SCRIPT_DIR/auto-commit.sh" || echo "Auto-commit exited with error (will retry on next change)"

    LAST_COMMIT_TIME=$(date +%s)
    echo "---"
done
EOF

# Make executable
chmod +x ~/blogposts/watch-and-commit.sh

Understanding Debouncing

Why debounce?

Without debouncing, every single save would create a commit. If you’re actively editing a file, your editor might save every few seconds, creating dozens of commits per minute!

How it works:

The script tracks when the last commit occurred. If a change is detected within 5 seconds of the last commit, it skips that change. After you stop editing (5 seconds of inactivity), it commits all accumulated changes in one commit.

Test the Watcher Manually

# Run in one terminal
~/blogposts/watch-and-commit.sh

# In another terminal, make changes
echo "More content" >> ~/blogposts/test.md

# Watch the first terminal - you'll see it detect the change and commit!

Press Ctrl+C to stop the watcher when done testing.

Step 6: Create Systemd Service

Running the watcher manually is tedious. Let’s make it a background service that starts automatically.

Create Service File

# Create systemd user directory
mkdir -p ~/.config/systemd/user

# Create service file
cat > ~/.config/systemd/user/blogpost-watcher.service << 'EOF'
[Unit]
Description=Blogpost Auto-Commit Watcher
After=network.target

[Service]
Type=simple
ExecStart=%h/blogposts/watch-and-commit.sh
Restart=always
RestartSec=10

[Install]
WantedBy=default.target
EOF

Note: The %h specifier in systemd automatically resolves to your home directory, so no manual path editing is needed. Logs are handled by journald — view them with journalctl --user -u blogpost-watcher.service -f.

Understanding the Service Configuration

Setting What It Does
Type=simple Service runs in foreground (doesn’t fork/daemonize)
ExecStart=%h/... Command to run (%h = your home directory)
Restart=always Automatically restart if it crashes
RestartSec=10 Wait 10 seconds before restarting
WantedBy=default.target Start when user logs in

★ Insight ─────────────────────────────────────
Systemd user services run in your user session, not as root. They start when you log in and stop when you log out (unless you enable “linger”). The %h specifier automatically resolves to your home directory, so you never need to hardcode paths. Logs go to journald by default — view them with journalctl --user -u blogpost-watcher.service. The Restart=always directive means if the watcher crashes, systemd automatically restarts it after 10 seconds, making the system self-healing.
─────────────────────────────────────────────────

Enable and Start Service

# Reload systemd to recognize new service
systemctl --user daemon-reload

# Enable service (start on login)
systemctl --user enable blogpost-watcher.service

# Start service now
systemctl --user start blogpost-watcher.service

# Check status
systemctl --user status blogpost-watcher.service

You should see output showing:

● blogpost-watcher.service - Blogpost Auto-Commit Watcher
     Loaded: loaded
     Active: active (running)

Step 7: Test the Complete System

Let’s verify everything works end-to-end.

Create a Test File

# Create a new blog post
cat > ~/blogposts/my-first-auto-commit-post.md << 'EOF'
# My First Auto-Committed Post

This post was automatically committed to GitHub!

## How It Works

As soon as I save this file, the inotify watcher detects the change,
waits 5 seconds to see if I make more changes, then automatically:

1. Commits the file to Git
2. Pushes to GitHub

No manual `git add`, `git commit`, or `git push` needed!
EOF

Watch the Logs

# View real-time logs via journald
journalctl --user -u blogpost-watcher.service -f

You should see output like:

🔍 Blogpost Auto-Commit Watcher Started
Watching: /home/your-username/blogposts
---
📝 Change detected: my-first-auto-commit-post.md
⏰ 2025-11-05 14:23:15
Waiting 5 seconds for additional changes...
Running auto-commit...
Changes detected in blogposts
Changes committed and pushed successfully
---

Verify on GitHub

  1. Go to https://github.com/your-username/blogposts
  2. You should see your new file!
  3. Click on “Commits” to see the auto-generated commit message

Managing the Service

View Service Status

systemctl --user status blogpost-watcher.service

View Live Logs

# View real-time logs via journald
journalctl --user -u blogpost-watcher.service -f

# View recent log entries
journalctl --user -u blogpost-watcher.service --since "1 hour ago"

Stop the Service

systemctl --user stop blogpost-watcher.service

Restart the Service

systemctl --user restart blogpost-watcher.service

Disable Auto-Start

systemctl --user disable blogpost-watcher.service

Re-enable Auto-Start

systemctl --user enable blogpost-watcher.service

Advanced Configurations

Adjust Debounce Time

If 5 seconds is too short or too long for your workflow, edit the watcher script:

nano ~/blogposts/watch-and-commit.sh

# Find this line:
DEBOUNCE_SECONDS=5

# Change to your preferred value (in seconds):
DEBOUNCE_SECONDS=10  # Wait 10 seconds
DEBOUNCE_SECONDS=30  # Wait 30 seconds
DEBOUNCE_SECONDS=2   # Wait 2 seconds (fast!)

# Save and restart the service
systemctl --user restart blogpost-watcher.service

Watch Multiple Directories

To watch multiple directories, create separate services for each:

# Copy the watcher script
cp ~/blogposts/watch-and-commit.sh ~/notes/watch-and-commit.sh

# Edit to point to new directory
nano ~/notes/watch-and-commit.sh
# Change WATCH_DIR and SCRIPT_DIR

# Create new service file
cp ~/.config/systemd/user/blogpost-watcher.service 
   ~/.config/systemd/user/notes-watcher.service

# Edit service to point to new script
nano ~/.config/systemd/user/notes-watcher.service

# Enable and start
systemctl --user daemon-reload
systemctl --user enable notes-watcher.service
systemctl --user start notes-watcher.service

Custom Commit Messages

To customize commit messages, edit auto-commit.sh:

nano ~/blogposts/auto-commit.sh

# Find the COMMIT_MSG line and modify:
COMMIT_MSG="Auto-save: $(date '+%Y-%m-%d %H:%M:%S')"
# Or
COMMIT_MSG="📝 Updated $(git diff --name-only --cached | wc -l) file(s)"
# Or include changed filenames
CHANGED_FILES=$(git diff --name-only --cached | tr 'n' ', ')
COMMIT_MSG="Auto-commit: Updated $CHANGED_FILES"

Exclude Specific File Patterns

To exclude additional files from being watched:

nano ~/blogposts/watch-and-commit.sh

# Find the --exclude line and add patterns:
--exclude '(.git|.swp|.log$|~$|.DS_Store|drafts/.*|private/.*)'

Common patterns to exclude:

  • drafts/.* – Ignore drafts folder
  • private/.* – Ignore private folder
  • .*.draft.md – Ignore files ending in .draft.md
  • TODO.md – Ignore specific files

Enable Linger (Run After Logout)

By default, user services stop when you log out. To keep them running:

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

# Now the service runs even after logout!

Troubleshooting

Issue: Service Not Starting

Check service status:

systemctl --user status blogpost-watcher.service

Common causes:

  1. Script path wrong: Verify paths in service file match your actual paths
  2. Script not executable: chmod +x ~/blogposts/watch-and-commit.sh
  3. inotify-tools not installed: sudo apt install inotify-tools

Issue: Changes Not Being Committed

Check if watcher is running:

systemctl --user status blogpost-watcher.service

View logs for errors:

journalctl --user -u blogpost-watcher.service --since "1 hour ago" --no-pager

Test auto-commit script manually:

# Make a change
echo "test" >> ~/blogposts/test.md

# Run script
~/blogposts/auto-commit.sh

# Check for errors

Common causes:

  1. Git authentication failed: Test git push manually
  2. File excluded by .gitignore: Check .gitignore patterns
  3. Debounce timer active: Wait 5+ seconds after last change

Issue: Push to GitHub Fails

Test authentication:

# For SSH
ssh -T git@github.com

# For HTTPS
git push origin main  # Should not prompt for password

Fix SSH authentication:

# Generate SSH key if needed (you will be prompted for a passphrase -
# use one for security, especially on shared/multi-user systems)
ssh-keygen -t ed25519 -C "your-email@example.com"

# Start ssh-agent and add your key (avoids re-entering passphrase)
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

# Add to GitHub
cat ~/.ssh/id_ed25519.pub
# Copy output and add to GitHub Settings → SSH Keys

Fix HTTPS authentication:

# Cache credentials in memory for 1 hour (safer than 'store' which saves in plaintext)
git config --global credential.helper 'cache --timeout=3600'

# Next push will prompt for credentials, then cache them in memory

Security note: Avoid credential.helper store — it writes your GitHub credentials to ~/.git-credentials in plaintext. On shared or multi-user systems, any user who can read that file gets your GitHub access. Use cache (memory-only) or SSH keys instead.

Issue: Too Many Commits

If you’re getting commits too frequently:

  1. Increase debounce time: Edit DEBOUNCE_SECONDS in watcher script
  2. Switch to scheduled commits: Use cron instead (see Alternative Approaches below)
  3. Manually commit instead: Disable service and run auto-commit.sh when ready

Issue: Service Stops After Logout

Enable linger:

sudo loginctl enable-linger $USER

Issue: High CPU Usage

Check inotify watch limit:

# View current limit
cat /proc/sys/fs/inotify/max_user_watches

# If watching many files, increase limit
echo "fs.inotify.max_user_watches=524288" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Alternative Approaches

Option 1: Cron-Based (Scheduled)

If real-time commits are too frequent, use cron for scheduled commits:

# Edit crontab
crontab -e

# Add line to run every hour (with SSH agent for key-based auth)
0 * * * * export HOME=$(getent passwd $(whoami) | cut -d: -f6) && export PATH=/usr/local/bin:/usr/bin:/bin:$PATH && eval "$(ssh-agent -s)" > /dev/null && ssh-add $HOME/.ssh/id_ed25519 2>/dev/null && $HOME/blogposts/auto-commit.sh >> $HOME/blogposts/cron.log 2>&1

# Or every 30 minutes
*/30 * * * * export HOME=$(getent passwd $(whoami) | cut -d: -f6) && $HOME/blogposts/auto-commit.sh >> $HOME/blogposts/cron.log 2>&1

# Or daily at 6 PM
0 18 * * * export HOME=$(getent passwd $(whoami) | cut -d: -f6) && $HOME/blogposts/auto-commit.sh >> $HOME/blogposts/cron.log 2>&1

Note: Cron runs in a minimal environment where $HOME, PATH, and SSH agent may not be available. The examples above set these explicitly. If using SSH authentication, the hourly example shows how to start ssh-agent in the cron context.

Pros: Batches changes, predictable schedule
Cons: Not real-time, might lose work between runs

Option 2: Git Hooks

Use Git’s post-save hooks (requires editor integration):

# Create post-commit hook
cat > ~/blogposts/.git/hooks/post-commit << 'EOF'
#!/bin/bash
git push origin main
EOF

chmod +x ~/blogposts/.git/hooks/post-commit

Pros: Pushes after every manual commit
Cons: Requires manual commits, no automation

Option 3: Editor Integration

Many editors can run commands on save:

VS Code (settings.json):

{
  "runOnSave.commands": [
    {
      "match": ".*\.md$",
      "command": "~/blogposts/auto-commit.sh",
      "runIn": "terminal"
    }
  ]
}

Vim (.vimrc):

autocmd BufWritePost ~/blogposts/* !~/blogposts/auto-commit.sh

Security Considerations

Sensitive Files

Never auto-commit:

  • API keys or credentials
  • Private personal information
  • Files with secrets

Our .gitignore already blocks common secret file patterns (.env, *.pem, *.key, credentials*, etc.), and the auto-commit.sh script includes a pre-commit check that aborts if it detects staged files matching secret patterns. These are defense-in-depth layers — if one fails, the other catches it.

To add more exclusions:

echo "my-custom-secret.yaml" >> ~/blogposts/.gitignore

Private Repositories

For sensitive content, use private GitHub repositories:

  1. Create repository as Private on GitHub
  2. Only you can see the content
  3. Auto-commits still work the same way

SSH Key Security

Best practices:

  • Use passphrase-protected SSH keys
  • Use ssh-agent to avoid entering passphrase repeatedly
  • Limit SSH key permissions: chmod 600 ~/.ssh/id_ed25519
  • Different SSH keys for different purposes

Performance Considerations

Resource Usage

The watcher service is extremely lightweight:

  • CPU: Nearly 0% when idle, <1% during commits
  • Memory: ~5-10 MB
  • Disk I/O: Minimal (only during actual commits)

Scalability

This system works well for:

  • ✅ Hundreds of small files (< 1 MB each)
  • ✅ Frequent small changes
  • ✅ Text files (markdown, code, configs)

Not recommended for:

  • ❌ Large binary files (videos, images > 10 MB)
  • ❌ Thousands of files changing simultaneously
  • ❌ Generated build output (node_modules, dist/)

Git Repository Size

Monitor repository size:

cd ~/blogposts
git count-objects -vH

If repository gets too large:

# Clean up old commits (advanced - be careful!)
git gc --aggressive --prune=now

Real-World Use Cases

Use Case 1: Daily Blog Writing

Scenario: Writing technical blog posts daily.

Configuration:

  • Debounce: 10 seconds (allows for thought between paragraphs)
  • Excludes: drafts/ folder for work-in-progress
  • Result: Every blog post automatically backed up within seconds

Use Case 2: Configuration Management

Scenario: Managing dotfiles and system configs.

Setup:

# Watch home directory configs
WATCH_DIR="$HOME"
# Only monitor specific files
--include '(.bashrc|.vimrc|.gitconfig)'

Result: Configuration changes automatically version controlled

Use Case 3: Note-Taking System

Scenario: Using Obsidian or similar markdown notes.

Configuration:

  • Watch Obsidian vault directory
  • Debounce: 30 seconds (longer gaps between note sessions)
  • Exclude: .obsidian/ folder (Obsidian metadata)

Result: Personal knowledge base continuously backed up

Use Case 4: Documentation Wiki

Scenario: Team wiki or documentation site.

Setup:

  • Private GitHub repository
  • Multiple users with SSH keys
  • Each user runs their own watcher

Result: Distributed documentation with automatic sync

Comparison with Other Solutions

vs. Manual Git Commits

Feature Auto-Commit Manual
Effort Zero High
Frequency Real-time When you remember
Commit Quality Generic messages Descriptive messages
Best For Personal projects Collaborative projects

vs. Cloud Sync (Dropbox, Google Drive)

Feature Auto-Commit Cloud Sync
Version History Full Git history Limited versions
Branching Yes No
Offline Work Yes Limited
Conflicts Git merge Auto-resolve (may lose data)
Cost Free (GitHub) Paid tiers

vs. Backup Software

Feature Auto-Commit Backup Software
Granularity File-level Often full disk
Restoration Any commit Backup points
Remote Access GitHub (anywhere) Backup location only
Speed Instant Scheduled

Conclusion

You’ve now built a fully automated Git commit system that:

  • ✅ Watches files for changes in real-time
  • ✅ Automatically commits with timestamps
  • ✅ Pushes to GitHub within seconds
  • ✅ Runs as a background service
  • ✅ Restarts automatically if it crashes
  • ✅ Detects and blocks accidental secret commits
  • ✅ Prevents concurrent commit race conditions
  • ✅ Logs all activity via journald for troubleshooting

Key Takeaways

  1. inotify is powerful: Linux’s filesystem monitoring is efficient and reliable
  2. Debouncing matters: Prevents commit spam during active editing
  3. Systemd is your friend: User services make background tasks simple
  4. Automation reduces friction: Remove barriers to backing up your work

Next Steps

Now that you have this working, consider:

  • Set up multiple watchers: Different directories for different projects
  • Customize commit messages: Add more context about what changed
  • Monitor via notifications: Send alerts when commits happen
  • Integrate with CI/CD: Trigger builds or deployments on commits

Further Resources

  • inotify-tools Documentation
  • systemd User Services
  • Git Documentation
  • GitHub SSH Setup

Questions or Issues? The most common problems are:

  1. Authentication (ensure SSH key is added to GitHub)
  2. Service not starting (check paths in service file)
  3. Too many commits (increase debounce time)

Happy auto-committing! 🚀


This guide was written based on a real-world implementation for a personal blog post repository. All configurations have been tested and verified working on Ubuntu 22.04 with Git 2.34+ and inotify-tools 3.22+.

Written by Michael Henry

Post navigation

Previous: NFS Mac DAS To Proxmox Tutorial
Next: Accessing Your Linux VM from Mac Finder: A Complete Guide to Samba File Sharing
Michael Henry

Michael Henry

© 2026 Digital Javelina, LLC