npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

schedic

v0.9.7

Published

Schedule restic backups for multiple hosts and/or repos

Downloads

3

Readme

schedic - restic on steroids

pipeline status coverage report

schedic logo

schedic makes it easy to backup multiple hosts in your network using the fantastic restic tool.

Everything is put into one YAML configuration file on your backup server. There is no schedic configuration on the hosts you like to backup. They require just an installation of restic. The backup server executes restic remotely using SSH and passes all required information in a secure fashion.

restic is an ultra fast backup tool which performs super efficient deduplication and encryption of your backup data. It stores data locally or uploads to multiple different storage backends.

Contents

Installation

Prerequisites

schedic requires Node.js (minimum v8.x). It's recommended to use Yarn for installation, but npm is supported as well.

As well bash is required on the schedic host and on the target hosts. That's no problem with Unix operating systems, but additionals steps may be necessary to use schedic with Windows and probably Mac (bash is default shell since Mac OS 10.3).

schedic uses ssh (and assumes OpenSSH) to execute restic commands on the target hosts. By default it sets -o 'BatchMode Yes' to prevent interactive password prompts which causes the process to hang or wait infinitely. If you don't use OpenSSH and require different options to accomplish this you can specify them in the host-defaults configuration section (see below);

From npm registry

# Using Yarn
yarn global add schedic

# Or npm (but without package locking)
npm -g install schedic

Building from source

This will transpile the source from TypeScript to JavaScript and then links the schedic script globally.

# Using yarn
yarn && yarn build
yarn link

# Using npm (but without package locking)
npm install && npm run build
npm link

Automatic TypeScript transpilation

During development you should open a shell running a nodemon watcher for automatic transpilation:

# Using yarn
yarn watch

# Using npm
npm run watch

Usage

schedic command [options]

Options

  • --config|c file.yml: specify configuration file, Default: ./schedic.yml, Environment: SCHEDIC_CONFIG
  • --dry-run: just print commands, which would be executed
  • --repo|r name: perform operations just for this repo, Default: all repos in configuration file
  • --host|h name: perform operations just for this host, Default: all hosts in configuration file
  • --path|p path: perform operation just for this backup path, Default: all paths in configuration file
  • --sync-output: print log immediately, even if stdout is redirected

Commands

run

Performs a combined execution of the backup and forget commands. Refer to their descriptions below.

Example:

# Perform backup, forget & prune of everything in ./schedic.yml
schedic run

backup

Performs backups for all selected hosts and paths in the configuration file.

Examples:

# Backup all paths of all hosts listed in ./schedic.yml
schedic backup

# Backup all paths of host "homesrv"
schedic backup --host homesrv

# Backup /home of host "homesrv"
schedic backup --host homesrv --path /home

# Backup /home of host "homesrv" to repo "mycloud" only
schedic backup --host homesrv --path /home --repo mycloud

forget

Removes backups for all selected hosts and paths in the configuration file according to the configured forget policies. Afterwards restic prune is executed on the affected repos.

Examples:

# Remove all backups according to the configured "forget" policies
schedic forget

# Remove all backups of host "homesrv" according to the "forget" policies
schedic forget --host homesrv

# Remove all "/home" backups of host "homesrv" according to the "forget" policies
schedic forget --host homesrv --path /home

check

This command checks if all selected hosts are accessable using SSH and if the restic command is available (by executing restic version).

Examples:

# Check all configured hosts
schedic check

# Check host "homesrv"
schedic check --host homesrv

update

Runs restic self-update on all selected hosts.

Examples:

# Update restic on all configured hosts
schedic update

# Update restic on host "homesrv"
schedic update --host homesrv

repo

Prints shell code to set your environment to a specific restic repo.

Example:

# Select repository "mycloud"
eval `schedic repo mycloud`

# List all snapshots in selected repo
restic snapshots

Backup configuration

All backups for all hosts are configured in a single YAML file. schedic validates the schema and prints specific error messages, if the configuration file is invalid.

This is an annotated example configuration:

# Version of the configuration
version: "2.0"

# Repo definition
# ---------------
# The following properties are known for a repository:
# - name: an arbitrary name
# - url: repository URL
# - password-file: path to a file containing the repo password
# - password: or the repo password itself (not recommended)
# - restic: an object of additional restic options
#   use boolean values for switches, and numeric/string values
#   for options with parameters
# - env: required environment variables (e.g. auth keys)
#   keys may be prefixed with "<<" to express that the
#   value is a filename where the env var is read from
#   (to keep secrets out of this file; eol removed).
#   env variables can be overridden by host-defaults and
#   host sections beyond.
repos:
  - name: mycloud
    url: "rest:https://mycloud.example.com"
    password-file: "test/assets/.restic-mycloud.pw"
    restic:
      limit-upload: 900

  - name: local
    url: "/storage/backup/restic"
    password: "super secret local restic password"

  - name: s3
    url: "s3:s3.amazonaws.com/myresticbucket"
    password: "mys3awsresticpassword"
    env:
      AWS_ACCESS_KEY_ID: myawsaccesskey
      <<AWS_SECRET_ACCESS_KEY: test/assets/.restic.myawssecretaccesskey.pw

# Default Host Options
# --------------------
# Specify default options for all backup hosts. Can be
# overridden by each host.
# - ssh: the ssh command to use (e.g. for a non-default port)
#   or false, if command should be executed locally;
#   the "BatchMode" option is important to prevent hanging
#   of the process in interactive password prompts
# - arch: the CPU architecture (yet unused)
# - restic: see "repos[].restic"
# - env: see "repos[].env"
host-defaults:
  ssh: "ssh -o 'BatchMode Yes'"
  arch: linux_amd64
  restic:
    limit-upload: 500

# Default Backup Options
# ----------------------
# Specify default options for all backups. Can be overridden
# by each backup.
# - repos: list of repos to push this backup to
# - exclude: list of paths resp. globs to exclude from the backup
# - keep: your "forget" policy. All restic --keep-* options are supported
# - restic: see "repos[].restic"
backup-defaults:
  repos:
    - local
  exclude:
    - .gvfs
    - .thumbnails/
    - .cache/
  keep:
    daily: 3
    weekly: 4
    monthly: 6
  restic:
    one-file-system: true

# Host & Backup definition
# ------------------------
# Specify all hosts and paths to be backed up.
# - name: network hostname
# - pre: a list of commands to execute prior backup
# - ssh: the ssh command to use (e.g. for a non-default port)
#   or false, if command should be executed locally
# - arch: the CPU architecture (yet unused)
# - disabled: set to true, to disable a host (temporarily)
# - restic: see "repos[].restic"
# - env: see "repos[].env"
hosts:
  - name: homesrv
    ssh: false
    pre:
      - "cd /home/gitlab && ./mkbackup && mv $(ls -1atr /home/gitlab/data/gitlab/backups/* | tail -1) /home/backup/gitlab-latest.tar"
    env:
      PATH: /opt/restic/bin

    # Backup definition
    # -----------------
    # Specify all backup paths for this host. All settings here
    # are merged with the "backup-defaults" specified above.
    # - path: the local directory path
    # - tags: single tag or list of tags
    # - repos: list of repos to push this path to
    # - exclude: list of paths resp. globs to exclude from the backup
    # - restic: see "repos.restic"
    backups:
      - path: /home
        tags: important
        repos:
          - mycloud
          - local
          - s3
        exclude:
          - /home/gitlab
      - path: /etc
        tags: etc
      - path: /root
        tags: root-home
      - path: /var
        tags: var
        exclude:
          - /var/cache
          - /var/lib/docker
          - /var/log
      - path: /
        tags: system
        exclude:
          - /root
          - /var
      - path: /mnt/music
        tags: music
        keep:
          weekly: 2

  - name: raspberry1
    arch: linux_arm
    backups:
      - path: /etc
        tags: etc
      - path: /var
        tags: var
        exclude:
          - /var/cache
          - /var/log
      - path: /
        tags: system
        exclude:
          - /var

  - name: raspberry2
    arch: linux_arm
    disabled: true
    backups:
      - path: /etc
        tags: etc
      - path: /var
        tags: var
        exclude:
          - /var/cache
          - /var/log
      - path: /
        tags: system
        exclude:
          - /var

Cron configuration

This is an example cron configuration for regular operation:

# Daily backup and removal
11 3 * * * root schedic --config /usr/local/etc/schedic.yml run 2>&1 | mail -s "Daily schedic report" [email protected]

# Weekly restic self-update
1 3 * * 0 root schedic --config /usr/local/etc/schedic.yml update 2>&1 | mail -s "Weekly schedic update report" [email protected]

Example output of summaries

schedic prints summaries of all executed tasks. If the output is no terminal, all log messages are buffered and the summaries are printed first. This is very useful when you send the output with e-mail, so everything important is in front.

This is an example output of schedic run --dry-run with the shipped example schedic.yml (log messages are removed):

> Backup summary
> OK: All tasks successfully completed
╔════════════╤═════════╤═════════╤═══════════════════════════╤════╤══════╤══════════╤═════════╗
║ Host       │ Type    │ Repo    │ Details                   │ ID │ Size │ Duration │ Result  ║
╟────────────┼─────────┼─────────┼───────────────────────────┼────┼──────┼──────────┼─────────╢
║ homesrv    │ Command │         │ cd /home/gitlab && ./m... │    │      │    -     │ Dry run ║
║ homesrv    │ Backup  │ mycloud │ /home                     │ -  │ -    │    -     │ Dry run ║
║ homesrv    │ Backup  │ local   │ /home                     │ -  │ -    │    -     │ Dry run ║
║ homesrv    │ Backup  │ local   │ /etc                      │ -  │ -    │    -     │ Dry run ║
║ homesrv    │ Backup  │ local   │ /root                     │ -  │ -    │    -     │ Dry run ║
║ homesrv    │ Backup  │ local   │ /var                      │ -  │ -    │    -     │ Dry run ║
║ homesrv    │ Backup  │ local   │ /                         │ -  │ -    │    -     │ Dry run ║
║ homesrv    │ Backup  │ local   │ /mnt/music                │ -  │ -    │    -     │ Dry run ║
╟────────────┼─────────┼─────────┼───────────────────────────┼────┼──────┼──────────┼─────────╢
║ raspberry1 │ Backup  │ local   │ /etc                      │ -  │ -    │    -     │ Dry run ║
║ raspberry1 │ Backup  │ local   │ /var                      │ -  │ -    │    -     │ Dry run ║
║ raspberry1 │ Backup  │ local   │ /                         │ -  │ -    │    -     │ Dry run ║
╚════════════╧═════════╧═════════╧═══════════════════════════╧════╧══════╧══════════╧═════════╝

> Forget summary
> OK: All tasks successfully completed
╔════════════╤═════════╤═══════════════════════════╤══════╤═════════╤═════════╗
║ Host       │ Repo    │ Path                      │ Kept │ Removed │ Result  ║
╟────────────┼─────────┼───────────────────────────┼──────┼─────────┼─────────╢
║ homesrv    │ mycloud │ /home                     │ -    │ -       │ Dry run ║
║ homesrv    │ local   │ /home                     │ -    │ -       │ Dry run ║
║ homesrv    │ local   │ /etc                      │ -    │ -       │ Dry run ║
║ homesrv    │ local   │ /root                     │ -    │ -       │ Dry run ║
║ homesrv    │ local   │ /var                      │ -    │ -       │ Dry run ║
║ homesrv    │ local   │ /                         │ -    │ -       │ Dry run ║
║ homesrv    │ local   │ /mnt/music                │ -    │ -       │ Dry run ║
╟────────────┼─────────┼───────────────────────────┼──────┼─────────┼─────────╢
║ raspberry1 │ local   │ /etc                      │ -    │ -       │ Dry run ║
║ raspberry1 │ local   │ /var                      │ -    │ -       │ Dry run ║
║ raspberry1 │ local   │ /                         │ -    │ -       │ Dry run ║
╚════════════╧═════════╧═══════════════════════════╧══════╧═════════╧═════════╝

> Prune summary
> OK: All tasks successfully completed
╔═════════╤══════════╤═════════╗
║ Repo    │ Duration │ Result  ║
╟─────────┼──────────┼─────────╢
║ mycloud │ -        │ Dry run ║
║ local   │ -        │ Dry run ║
╚═════════╧══════════╧═════════╝

Security considerations

A few words about security.

Storing restic password

restic requires a password to encrypt all backup data. You have two options to store this passsord with schedic:

  1. in a separate text file
  2. directly in the schedic.yml file

It's recommended to use option 1) and important to mention, that only the schedic server has to know the password. There is no need to store this password on the hosts you back up. schedic will pass the password through SSH using a pipe, so it's even not visible in the processes environment.

You should use restricted permissions on the password file.

The advantage to not having the password in the schedic.yml is that it's easier to not disclosure it by accident - e.g. by showing someone else your schedic configuration or posting questions to stackoverflow ;)

SSH access to all hosts

In terms of security it's not optimal that the schedic server requires passwordless (key based) root access to all your hosts you want to back up. A person who gains access to your schedic server and the account you're running schedic with has also root access to all your hosts.

So you really have to take care to protect your schedic server.

On the other hand you can still make use of schedic if this open SSH configuration is no option for you. You could install schedic on all hosts and run it locally (besides restic you have to install anyway on every host). At least you have the benefit of schedic's nice configuration file and reporting features.

CI pipeline

This project uses a simple GitLab CI pipeline to ensure code quality (linting and unit testing) and that the code is buildable. Deployment is a manual step I perform from my workstation.

The pipeline can be simply executed locally just by launching ci/local.sh from within the project directory. A temporary Docker container is created to execute all steps (yarn install, yarn lint, yarn cover). A few tests depend on the restic binary, which is downloaded (and cached) automatically from GitHub.

Publishing a new release

# add entry to CHANGELOG.md and
# add all files to be commited along with release tag
git add CHANGELOG.md README.md

# set new version (commits package.json and all added files)
yarn version [--patch]

# publish to npm registry
yarn publish

License

Copyright (c) 2019 Jörn Reder

MIT License

See LICENSE file in this directory.

TODO / IDEAS

  • ping log messages for long running commands

  • configuration

    • new key host.repos
    • overrides backup-defaults.repos if present
  • put some repo stats at top of mail report

  • commands

    • schedic install -- install restic on target hosts
    • schedic snapshots -- list snapshots for selected backups
    • schedic setup -- simple wizard to setup a basic schedic.yml