Obsidian + Github Actions

Obsidian Sync alternative?

Section 1 - Background

I use Obsidian as my note taking app of choice.

For a time I bought into the Sync membership which worked really well.

There was a few issues however: - Synced everything which made my mobile experience slow. - I only needed a small portion of notes. - I was only using my vault on my desktop as a result of it being slow on mobile - I was paying for not really using Sync….

I could just use a Cloud service to sync my notes but as I am cross platforms, that doesn’t always make it easy. I also don’t want my notes in the Cloud, clogging up space when they can be locally.

This lead me to look for solutions.

Initally I was just using Git to make sure my notes were backed up and I had version history but it didn’t provide a ‘Sync’ where I could only sync a specific folder. Syncthing was unfortunately off the cards due to OS limitations.

But then I got to thinking…

Section 2 - Github Actions

Key words

CI/CD = Continuous integration and continuous delivery repo = Repository

In Git you have the ability to automate the running of tests, application builds, notifications etc.

After watching some Ben Vallack’s Youtube videos and getting inspired to use AI to create my own things I thought why couldn’t I use Github Actions to sync a specific folder between two repositories, automatically. Essentailly creating my Obsidian Sync.

This so far has worked really well and allows me to have a lightweight vault on my phone and tablet, allowing me to take the full advantages of Obsidian without the heft of my ‘main vault’.

Section 3 - How.

Github actions uses yaml or yml format which is two spaces for every indentation.

  • Create two Github repos, one repo will be for your ‘main vault’ and the second will be your ‘mboile vauilt’

  • Once created, select the Actions tab on your repo.

    • Select ‘Set up a workflow yourself’
name: Sync Mobile to Desktop (Last-Write-Wins)
on:
  push:
    paths:
      - '' # Path to the folder you wish to Sync.
  workflow_dispatch:
  repository_dispatch:
    types: [sync-from-desktop]

jobs:
  sync:
    runs-on: ubuntu-latest
    # Allows manaul triggers, cross-repo triggers and blocks auto-sync commits. 
    if: |
      github.event_name == 'workflow_dispatch' || 
      github.event_name == 'repository_dispatch' ||
      (github.event_name == 'push' && !contains(github.event.head_commit.message, '[auto-sync]'))
    
    steps:
      - name: Checkout mobile repo
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          token: ${{ secrets.GH_TOKEN }}
      
      - name: Configure Git
        run: |
          git config --global user.name "github-actions[bot]"
          git config --global user.email "github-actions[bot]@users.noreply.github.com"
      
      - name: Clone and sync with desktop repo
        env:
          GH_TOKEN: ${{ secrets.GH_TOKEN }}
        run: |
          # Clone desktop vault
          git clone https://${GH_TOKEN}@github.com/YOUR_USER/YOUR_Repo.git desktop-temp
          
          cd desktop-temp
          
          # Create a branch for our changes
          git checkout -b sync-from-mobile
          
          # Copy files from mobile to desktop
          rsync -av --delete --exclude='.*' ../satellite/ ./satellite/
          
          # Check if there are changes
          if [[ -z $(git status -s) ]]; then
            echo "No changes to sync"
            exit 0
          fi
          
          # Stage all changes
          git add .
          
          # Commit changes
          git commit -m "[auto-sync] Sync from mobile vault"
          
          # Try to merge with main
          git fetch origin main
          git checkout main
          git pull origin main
          
          # Attempt to merge our sync branch
          if git merge sync-from-mobile --no-edit; then
            echo "Clean merge successful"
            git push origin main
          else
            echo "⚠️ MERGE CONFLICT DETECTED - Using last-write-wins strategy"
            
            # Get list of conflicted files
            CONFLICTED_FILES=$(git diff --name-only --diff-filter=U)
            
            echo "Conflicted files:"
            echo "$CONFLICTED_FILES"
            
            CONFLICT_SUMMARY=""
            
            for file in $CONFLICTED_FILES; do
              if [[ -f "$file" ]]; then
                # Get timestamps for both versions
                # Mobile version (from our branch)
                git show sync-from-mobile:"$file" > /tmp/mobile-version 2>/dev/null || continue
                MOBILE_TIME=$(stat -c %Y /tmp/mobile-version 2>/dev/null || stat -f %m /tmp/mobile-version)
                
                # Desktop version (from main)
                git show main:"$file" > /tmp/desktop-version 2>/dev/null || continue
                DESKTOP_TIME=$(stat -c %Y /tmp/desktop-version 2>/dev/null || stat -f %m /tmp/desktop-version)
                
                echo "File: $file"
                echo "  Mobile timestamp: $MOBILE_TIME"
                echo "  Desktop timestamp: $DESKTOP_TIME"
                
                if [[ $MOBILE_TIME -gt $DESKTOP_TIME ]]; then
                  echo "  → Mobile version is newer, keeping mobile"
                  # Only create backup for the losing version (desktop in this case)
                  BACKUP_FILE="${file}.desktop-backup-$(date +%Y%m%d-%H%M%S)"
                  git show main:"$file" > "$BACKUP_FILE" 2>/dev/null || true
                  git checkout --ours "$file"
                  git add "$file" "$BACKUP_FILE"
                  CONFLICT_SUMMARY="${CONFLICT_SUMMARY}\n- $file: Mobile version kept (newer), desktop backed up"
                else
                  echo "  → Desktop version is newer, keeping desktop"
                  # Only create backup for the losing version (mobile in this case)
                  BACKUP_FILE="${file}.mobile-backup-$(date +%Y%m%d-%H%M%S)"
                  cp /tmp/mobile-version "$BACKUP_FILE" 2>/dev/null || true
                  git checkout --theirs "$file"
                  git add "$file" "$BACKUP_FILE"
                  CONFLICT_SUMMARY="${CONFLICT_SUMMARY}\n- $file: Desktop version kept (newer), mobile backed up"
                fi
                
                rm -f /tmp/mobile-version /tmp/desktop-version
              fi
            done
            
            # Complete the merge
            git commit -m "[auto-sync] Merge from mobile (conflicts resolved via last-write-wins)"
            git push origin main
            
            # Create an issue to notify about conflicts
            echo "Creating notification issue about conflicts..."
            gh issue create \
              --title "⚠️ Sync Conflicts Resolved (Last-Write-Wins)" \
              --body "Conflicts were detected and resolved using last-write-wins strategy:${CONFLICT_SUMMARY}" \
              --label "sync-conflict" 2>/dev/null || echo "Could not create issue"
          fi
          
          cd ..
          rm -rf desktop-temp
      
      - name: Trigger desktop repo to sync back
        if: success() && github.event_name != 'repository_dispatch'
        env:
          GH_TOKEN: ${{ secrets.GH_TOKEN }}
        run: |
          curl -X POST \
            -H "Accept: application/vnd.github+json" \
            -H "Authorization: Bearer ${GH_TOKEN}" \
            -H "X-GitHub-Api-Version: 2022-11-28" \
            https://api.github.com/repos/0llieJ/ObsidianRepo/dispatches \
            -d '{"event_type":"sync-from-mobile"}'
  • Reverse the code so it applies for the Desktop so when the desktop vault makes a change, that will get pushed to the mobile repo.

  • Configure a Github Access Token with the correct permissions.

    • repo (Full scope)
    • workflow (Full scope)
  • Setup Obsidian Git plugin to sync your changes.


Conclusion

This is has been relatively new to me, but seems to be working well and provides all the sync features I need for free, minus anything I don’t need. You can always expand the folder you want to include and sync, by adding new lines with relavant folder paths and updating the rsync line.

See you in the next one!