Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ansible/dotfiles.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@
- paperless
- never

- name: Include paperless backup setup tasks
include_tasks: tasks/paperless_backup.yml
tags:
- paperless-backup
- never

- name: Include dotfile linking tasks
include_tasks: tasks/link_files.yml
tags:
Expand Down
40 changes: 40 additions & 0 deletions ansible/tasks/paperless_backup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
- name: Install restic via Homebrew
community.general.homebrew:
name: restic
state: present

- name: Create paperless backup staging directory
ansible.builtin.file:
path: "{{ ansible_user_dir }}/.paperless-backup-staging"
state: directory
mode: "0700"

- name: Copy paperless backup launchd plist
ansible.builtin.copy:
src: "{{ ansible_user_dir }}/.dotfiles/paperless-ngx/uk.me.michaelbarton.paperless-backup.plist"
dest: "{{ ansible_user_dir }}/Library/LaunchAgents/uk.me.michaelbarton.paperless-backup.plist"
mode: "0644"

- name: Load paperless backup launch agent
ansible.builtin.command:
cmd: launchctl load "{{ ansible_user_dir }}/Library/LaunchAgents/uk.me.michaelbarton.paperless-backup.plist"
args:
creates: "{{ ansible_user_dir }}/Library/LaunchAgents/uk.me.michaelbarton.paperless-backup.plist"

# One-time manual setup required (run these once before first backup):
#
# security add-generic-password -a $USER -s paperless-backup-b2-id -w '<keyID>'
# security add-generic-password -a $USER -s paperless-backup-b2-key -w '<applicationKey>'
# security add-generic-password -a $USER -s paperless-backup-restic-pw -w '<strong-random-password>'
#
# IMPORTANT: Also save the restic password in 1Password — lose it and backups are unrecoverable.
#
# Then initialise the restic repo once:
# set -x B2_ACCOUNT_ID (security find-generic-password -a $USER -s paperless-backup-b2-id -w)
# set -x B2_ACCOUNT_KEY (security find-generic-password -a $USER -s paperless-backup-b2-key -w)
# set -x RESTIC_PASSWORD (security find-generic-password -a $USER -s paperless-backup-restic-pw -w)
# restic -r b2:mb-paperless-backup:paperless init
#
# Also restart the paperless stack to pick up the new volume mount:
# docker compose -f ~/.dotfiles/paperless-ngx/docker-compose.yml up -d
36 changes: 36 additions & 0 deletions paperless-ngx/backup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env bash
set -euo pipefail

export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"

LOG_FILE="$HOME/Library/Logs/paperless-backup.log"
STAGING_DIR="$HOME/.paperless-backup-staging"
COMPOSE_FILE="$HOME/.dotfiles/paperless-ngx/docker-compose.yml"

log() {
echo "[$(date '+%Y-%m-%dT%H:%M:%S')] $*" | tee -a "$LOG_FILE"
}

log "Starting paperless backup"

export B2_ACCOUNT_ID
B2_ACCOUNT_ID=$(security find-generic-password -a "$USER" -s paperless-backup-b2-id -w)
export B2_ACCOUNT_KEY
B2_ACCOUNT_KEY=$(security find-generic-password -a "$USER" -s paperless-backup-b2-key -w)
export RESTIC_PASSWORD
RESTIC_PASSWORD=$(security find-generic-password -a "$USER" -s paperless-backup-restic-pw -w)

log "Clearing staging directory"
mkdir -p "$STAGING_DIR"
rm -rf "${STAGING_DIR:?}"/*

log "Running paperless document_exporter"
/Applications/Docker.app/Contents/Resources/bin/docker compose -f "$COMPOSE_FILE" exec -T webserver document_exporter -na -nt -f /usr/src/paperless/backup

log "Running restic backup"
restic -r b2:mb-paperless-backup:paperless backup "$STAGING_DIR"

log "Pruning old snapshots"
restic -r b2:mb-paperless-backup:paperless forget --keep-daily 7 --keep-weekly 2 --keep-monthly 2 --prune

log "Paperless backup complete"
1 change: 1 addition & 0 deletions paperless-ngx/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ services:
- /Users/michaelbarton/.paperless:/usr/src/paperless/media:rw
- /Users/michaelbarton/Downloads:/usr/src/paperless/export:rw
- /Users/michaelbarton/Documents/consume:/usr/src/paperless/consume:rw
- /Users/michaelbarton/.paperless-backup-staging:/usr/src/paperless/backup:rw
env_file: docker-compose.env
environment:
PAPERLESS_REDIS: redis://broker:6379
Expand Down
26 changes: 26 additions & 0 deletions paperless-ngx/uk.me.michaelbarton.paperless-backup.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>uk.me.michaelbarton.paperless-backup</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>/Users/michaelbarton/.dotfiles/paperless-ngx/backup.sh</string>
</array>
<key>RunAtLoad</key>
<false/>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>3</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>StandardOutPath</key>
<string>/Users/michaelbarton/Library/Logs/paperless-backup.log</string>
<key>StandardErrorPath</key>
<string>/Users/michaelbarton/Library/Logs/paperless-backup.log</string>
</dict>
</plist>
Loading