diff --git a/restic/files/restic.service.j2 b/restic/files/restic.service.j2
new file mode 100644
index 0000000000000000000000000000000000000000..3cf5b23ee32fb35ef9d014559f9ed7855e4be7d7
--- /dev/null
+++ b/restic/files/restic.service.j2
@@ -0,0 +1,37 @@
+{% set restic = pillar.get('restic', {}) -%}
+{% if accumulator is not defined -%}
+  {% set accumulator = {} -%}
+{% endif -%}
+{% set cmds = accumulator.get('restic.cmd', []) | json_query('[][]') -%}
+{% set paths = accumulator.get('restic.path', []) | json_query('[][]') -%}
+{% set excludes = accumulator.get('restic.exclude', []) | json_query('[][]') -%}
+
+[Unit]
+Description=Create a backup
+Wants=network-online.target
+After=network-online.target
+
+[Service]
+Type=oneshot
+Environment="RESTIC_REPOSITORY=rest:https://{{ restic.get('http_user') }}:{{ restic.get('http_password') }}@{{ restic.get('http_host') }}/{{ restic.get('http_user') }}"
+Environment="RESTIC_PASSWORD_FILE=/etc/restic.password"
+Environment="RESTIC_CACHE_DIR=/tmp/restic"
+{% for cmd in cmds | sort -%}
+{% if cmd[0] != '/' -%}
+    {% set cmd = cmd.split(' ') -%}
+    {% do cmd.insert(0, cmd[0] | which) -%}
+    {% do cmd.pop(1) -%}
+    {% set cmd = cmd|join(' ') -%}
+{% endif -%}
+ExecStartPre={{ cmd }}
+{% endfor -%}
+ExecStart={{ 'restic' | which }} backup \
+    {% for path in paths | sort -%}
+    "{{ path }}"{% if not loop.last or excludes %} \{%endif %}
+    {% else -%}
+    /dev/null{% if excludes %} \{%endif %}
+    {% endfor -%}
+    {% for path in excludes | sort -%}
+    --exclude "{{ path }}"{% if not loop.last %} \{%endif %}
+    {% endfor -%}
+
diff --git a/restic/files/restic.timer.j2 b/restic/files/restic.timer.j2
new file mode 100644
index 0000000000000000000000000000000000000000..187f0f154d25c77b588a73fc6a416f710bf5035a
--- /dev/null
+++ b/restic/files/restic.timer.j2
@@ -0,0 +1,14 @@
+{% set restic = pillar.get('restic', {}) -%}
+[Unit]
+Description=Create a backup
+Wants=network-online.target
+After=network-online.target
+
+[Timer]
+OnCalendar={{ restic.get('time', '02:00') }}
+RandomizedDelaySec={{ restic.get('delay', '7200') }}
+Persistent=true
+
+[Install]
+WantedBy=timers.target
+
diff --git a/restic/init.sls b/restic/init.sls
new file mode 100644
index 0000000000000000000000000000000000000000..075b59133ae33c35c459189d31fb795dbb07c934
--- /dev/null
+++ b/restic/init.sls
@@ -0,0 +1,86 @@
+{% set restic = pillar.get('restic', {}) -%}
+{% set backup = restic.get('backup', {}) %}
+
+restic:
+  pkg.installed: []
+
+restic password:
+  file.managed:
+    - name: /etc/restic.password
+    - user: root
+    - group: root
+    - mode: '0600'
+    - contents: {{ restic.get('password') }}
+
+{% set repository = ['rest:https://', restic.get('http_user'), ':', restic.get('http_password'), '@',  restic.get('http_host'), '/', restic.get('http_user')] | join %}
+restic init:
+  cmd.run:
+    - env:
+      - RESTIC_REPOSITORY: {{ repository }}
+      - RESTIC_PASSWORD_FILE: /etc/restic.password
+    - unless: restic snapshots -r '{{ repository }}' -p /etc/restic.password
+    - require:
+      - file: restic password
+      - pkg: restic
+
+/etc/systemd/system/restic.service:
+  file.managed:
+    - source: salt://restic/files/restic.service.j2
+    - template: jinja
+  service.enabled:
+    - name: restic
+    - require:
+      - file: /etc/systemd/system/restic.service
+
+/etc/systemd/system/restic.timer:
+  file.managed:
+    - source: salt://restic/files/restic.timer.j2
+    - template: jinja
+  service.running:
+    - name: restic.timer
+    - enable: True
+    - require:
+      - service: restic
+      - file: restic password
+      - cmd: restic init
+
+{% macro cmd(name) %}
+restic run {{ name }}:
+  file.accumulated:
+    - name: restic.cmd
+    - text: {{ varargs | list | tojson }}
+    - filename: /etc/systemd/system/restic.service
+    - require_in:
+      - file: /etc/systemd/system/restic.service
+{% endmacro %}
+
+{% macro path(name) %}
+restic backup {{ name }}:
+  file.accumulated:
+    - name: restic.path
+    - filename: /etc/systemd/system/restic.service
+    - text: {{ varargs | list | tojson }}
+    - require_in:
+      - file: /etc/systemd/system/restic.service
+{% endmacro %}
+
+{% macro exclude(name) %}
+restic exclude {{ name }}:
+  file.accumulated:
+    - name: restic.exclude
+    - filename: /etc/systemd/system/restic.service
+    - text: {{ varargs | list | tojson }}
+    - require_in:
+      - file: /etc/systemd/system/restic.service
+{% endmacro %}
+
+{% if backup.get('cmd', []) %}
+{{ cmd('defaults', *backup.get('cmd', [])) }}
+{% endif %}
+{% if backup.get('path', []) %}
+{{ path('defaults', *backup.get('path', [])) }}
+{% endif %}
+{% if backup.get('exclude', []) %}
+{{ exclude('defaults', *backup.get('exclude', [])) }}
+{% endif %}
+
diff --git a/restic/readme.md b/restic/readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..8810187962b63a755d72a44ebb1d7f2cb247ae06
--- /dev/null
+++ b/restic/readme.md
@@ -0,0 +1,49 @@
+# Restic
+This module backups the provided data to a [Restic](https://restic.readthedocs.io/en/latest/)
+[REST Server](https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#rest-server) over https.
+
+When no backup paths are specified, `/dev/null` is added to allow for the same rollout on every system.
+
+
+## Requirements
+* Python
+  * `jmespath`
+* Parameters
+  * The `cmd` command must start with a command which has to be an absolute path to a binary or exist in `$PATH`.
+
+
+## Pillar
+Pillar configuration to configure the backup target
+
+```yaml
+restic:
+  http_user: {{ grains['id'] }}
+  http_password: 1anDoMpAs5Wo1D
+  http_host: restic.host.tld
+  password: rAnD0MpA5SWoRd # The repository password
+  backup:
+    cmd: # Optional, commands to run before starting the backup
+      - echo 'I should be in the backup' > /tmp/note
+    path: # Paths to include in the backup
+      - /tmp/note
+      - /etc/
+    exclude: # Paths to exclude from backup
+      - /etc/foo/
+  time: 5:42 # Optional, time to start the backup, default 02:00
+  delay: 300 # Optional, in seconds, defaults 2h
+```
+
+
+## States
+Include backups by other states
+
+```jinja
+{% import 'restic/init.sls' as restic %}
+
+[...]
+
+{{ restic.cmd('cmd name', '/usr/local/bin/dump-data > /some/file') }}
+{{ restic.path('path name', '/some/file') }}
+{{ restic.exclude('exclude name', '/another/path') }}
+```
+