diff --git a/README.md b/README.md
index e08b871560..4f576a497a 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,7 @@ If you have a spare domain name you can configure applications to be accessible
## Available Applications
* [Airsonic](https://airsonic.github.io/) - catalog and stream music
+* [Barcode Buddy](https://github.com/Forceu/barcodebuddy/) - Barcode system for Grocy
* [Bazarr](https://github.com/morpheus65535/bazarr) - companion to Radarr and Sonarr for downloading subtitles
* [Bitwarden](https://github.com/dani-garcia/vaultwarden) - Password Manger (Technically Vaultwarden, a lightweight implementation in Rust)
* [Booksonic](https://booksonic.org/) - The selfhosted audiobook server
@@ -42,6 +43,7 @@ If you have a spare domain name you can configure applications to be accessible
* [Glances](https://nicolargo.github.io/glances/) - for seeing the state of your system via a web browser
* [Gotify](https://gotify.net/) - Self-hosted server for sending push notifications
* [Grafana](https://grafana.com/) - Query, visualize, alert on, and understand your data no matter where it’s stored (via stats role).
+* [Grocy](https://grocy.info/) - web-based self-hosted groceries & household management solution for your home
* [Guacamole](https://guacamole.apache.org/) - Web based remote desktop gateway, supports VNC, RDP and SSH
* [healthchecks.io](https://healthchecks.io/) - Ensure your NAS is online and get notified otherwise
* [Heimdall](https://heimdall.site/) - Home server dashboard
diff --git a/nas.yml b/nas.yml
index db4d157db0..75587ce8cc 100644
--- a/nas.yml
+++ b/nas.yml
@@ -149,6 +149,10 @@
- gotify
when: (gotify_enabled | default(False))
+ - role: grocy
+ tags:
+ - grocy
+
- role: guacamole
tags:
- guacamole
diff --git a/roles/grocy/defaults/main.yml b/roles/grocy/defaults/main.yml
new file mode 100644
index 0000000000..436ca70df7
--- /dev/null
+++ b/roles/grocy/defaults/main.yml
@@ -0,0 +1,37 @@
+---
+grocy_enabled: false
+grocy_bbuddy_enabled: false
+grocy_available_externally: false
+grocy_bbuddy_available_externally: false
+
+# directories
+grocy_data_directory: "{{ docker_home }}/grocy"
+
+# network
+grocy_port: "9283"
+grocy_hostname: "grocy"
+grocy_bbuddy_http_port: "9284"
+grocy_bbuddy_https_port: "9285"
+grocy_bbuddy_hostname: "barcodebuddy"
+
+# specs
+grocy_memory: 1g
+grocy_bbuddy_memory: 1g
+
+# docker
+grocy_container_name: "grocy"
+grocy_image: "lscr.io/linuxserver/grocy"
+grocy_tag: "latest"
+grocy_user_id: "1000"
+grocy_group_id: "1000"
+grocy_bbuddy_container_name: "barcodebuddy"
+grocy_bbuddy_image: "f0rc3/barcodebuddy"
+grocy_bbuddy_tag: "latest"
+grocy_bbuddy_user_id: "1000"
+grocy_bbuddy_group_id: "1000"
+
+# barcode buddy
+grocy_bbuddy_scanner: false
+grocy_bbuddy_ignore_ssl_ca: true
+grocy_bbuddy_ignore_ssl_host: true
+grocy_bbuddy_grocy_url: "https://{{ grocy_hostname }}.{{ ansible_nas_domain }}"
diff --git a/roles/grocy/molecule/default/molecule.yml b/roles/grocy/molecule/default/molecule.yml
new file mode 100644
index 0000000000..463ea9a3d6
--- /dev/null
+++ b/roles/grocy/molecule/default/molecule.yml
@@ -0,0 +1,7 @@
+---
+provisioner:
+ inventory:
+ group_vars:
+ all:
+ grocy_enabled: true
+ grocy_bbuddy_enabled: true
diff --git a/roles/grocy/molecule/default/side_effect.yml b/roles/grocy/molecule/default/side_effect.yml
new file mode 100644
index 0000000000..adfef8e82a
--- /dev/null
+++ b/roles/grocy/molecule/default/side_effect.yml
@@ -0,0 +1,11 @@
+---
+- name: Stop
+ hosts: all
+ become: true
+ tasks:
+ - name: "Include {{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }} role"
+ include_role:
+ name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}"
+ vars:
+ grocy_enabled: false
+ grocy_bbuddy_enabled: false
diff --git a/roles/grocy/molecule/default/verify.yml b/roles/grocy/molecule/default/verify.yml
new file mode 100644
index 0000000000..00fce2139f
--- /dev/null
+++ b/roles/grocy/molecule/default/verify.yml
@@ -0,0 +1,26 @@
+---
+- name: Verify
+ hosts: all
+ gather_facts: false
+ tasks:
+ - name: Include vars
+ include_vars:
+ file: ../../defaults/main.yml
+
+ - name: Get grocy container state
+ docker_container_info:
+ name: "{{ grocy_container_name }}"
+ register: result
+
+ - name: Get barcodebuddy container state
+ docker_container_info:
+ name: "{{ grocy_bbuddy_container_name }}"
+ register: result_bbuddy
+
+ - name: Check if grocy docker container is running
+ assert:
+ that:
+ - result.container['State']['Status'] == "running"
+ - result.container['State']['Restarting'] == false
+ - result_bbuddy.container['State']['Status'] == "running"
+ - result_bbuddy.container['State']['Restarting'] == false
diff --git a/roles/grocy/molecule/default/verify_stopped.yml b/roles/grocy/molecule/default/verify_stopped.yml
new file mode 100644
index 0000000000..f8c4fbf876
--- /dev/null
+++ b/roles/grocy/molecule/default/verify_stopped.yml
@@ -0,0 +1,26 @@
+---
+- name: Verify
+ hosts: all
+ gather_facts: false
+ tasks:
+ - name: Include vars
+ include_vars:
+ file: ../../defaults/main.yml
+
+ - name: Try and stop and remove grocy
+ docker_container:
+ name: "{{ grocy_container_name }}"
+ state: absent
+ register: result
+
+ - name: Try and stop and remove barcodebuddy
+ docker_container:
+ name: "{{ grocy_bbuddy_container_name }}"
+ state: absent
+ register: result_bbuddy
+
+ - name: Check if grocy containers are stopped
+ assert:
+ that:
+ - not result.changed
+ - not result_bbuddy.changed
diff --git a/roles/grocy/tasks/main.yml b/roles/grocy/tasks/main.yml
new file mode 100644
index 0000000000..c376b50389
--- /dev/null
+++ b/roles/grocy/tasks/main.yml
@@ -0,0 +1,89 @@
+---
+- name: Start Grocy
+ block:
+ - name: Create Grocy Directory
+ file:
+ path: "{{ item }}"
+ state: directory
+ with_items:
+ - "{{ grocy_data_directory }}"
+
+ - name: Grocy Docker Container
+ community.docker.docker_container:
+ name: "{{ grocy_container_name }}"
+ image: "{{ grocy_image }}:{{ grocy_tag }}"
+ pull: true
+ volumes:
+ - "{{ grocy_data_directory }}:/config:rw"
+ ports:
+ - "{{ grocy_port }}:80"
+ env:
+ TZ: "{{ ansible_nas_timezone }}"
+ PUID: "{{ grocy_user_id }}"
+ PGID: "{{ grocy_group_id }}"
+ labels:
+ traefik.enable: "{{ grocy_available_externally | string }}"
+ traefik.http.routers.grocy.rule: "Host(`{{ grocy_hostname }}.{{ ansible_nas_domain }}`)"
+ traefik.http.routers.grocy.tls.certresolver: "letsencrypt"
+ traefik.http.routers.grocy.tls.domains[0].main: "{{ ansible_nas_domain }}"
+ traefik.http.routers.grocy.tls.domains[0].sans: "*.{{ ansible_nas_domain }}"
+ traefik.http.middlewares.grocy-whitelist.ipwhitelist.ipstrategy.depth: "1"
+ traefik.http.middlewares.grocy-whitelist.ipwhitelist.sourcerange: "127.0.0.1/32, 192.168.0.0/16"
+ traefik.http.routers.grocy.middlewares: "grocy-whitelist"
+ traefik.http.services.grocy.loadbalancer.server.port: "80"
+ restart_policy: unless-stopped
+ memory: "{{ grocy_memory }}"
+
+ - name: Install Barcodebuddy
+ block:
+ - name: Create Barcodebuddy Directory
+ file:
+ path: "{{ item }}"
+ state: directory
+ with_items:
+ - "{{ grocy_data_directory }}/barcodebuddy"
+
+ - name: Barcodebuddy Docker Container
+ community.docker.docker_container:
+ name: "{{ grocy_bbuddy_container_name }}"
+ image: "{{ grocy_bbuddy_image }}:{{ grocy_bbuddy_tag }}"
+ pull: true
+ volumes:
+ - "{{ grocy_data_directory }}/barcodebuddy:/config:rw"
+ ports:
+ - "{{ grocy_bbuddy_http_port }}:80"
+ - "{{ grocy_bbuddy_https_port }}:443"
+ env:
+ TZ: "{{ ansible_nas_timezone }}"
+ PUID: "{{ grocy_bbuddy_user_id }}"
+ PGID: "{{ grocy_bbuddy_group_id }}"
+ ATTACH_BARCODESCANNER: "{{ grocy_bbuddy_scanner | string }}"
+ IGNORE_SSL_CA: "{{ grocy_bbuddy_ignore_ssl_ca | string }}"
+ IGNORE_SSL_HOST: "{{ grocy_bbuddy_ignore_ssl_host | string }}"
+ BBUDDY_EXTERNAL_GROCY_URL: "{{ grocy_bbuddy_grocy_url }}"
+ labels:
+ traefik.enable: "{{ grocy_bbuddy_available_externally | string }}"
+ traefik.http.routers.barcodebuddy.rule: "Host(`{{ grocy_bbuddy_hostname }}.{{ ansible_nas_domain }}`)"
+ traefik.http.routers.barcodebuddy.tls.certresolver: "letsencrypt"
+ traefik.http.routers.barcodebuddy.tls.domains[0].main: "{{ ansible_nas_domain }}"
+ traefik.http.routers.barcodebuddy.tls.domains[0].sans: "*.{{ ansible_nas_domain }}"
+ traefik.http.services.barcodebuddy.loadbalancer.server.port: "80"
+ restart_policy: unless-stopped
+ memory: "{{ grocy_bbuddy_memory }}"
+ when: grocy_bbuddy_enabled is true
+ when: grocy_enabled is true
+
+- name: Stop grocy
+ block:
+ - name: Stop grocy
+ community.docker.docker_container:
+ name: "{{ grocy_container_name }}"
+ state: absent
+
+ - name: Stop Barcodebuddy
+ community.docker.docker_container:
+ name: "{{ grocy_bbuddy_container_name }}"
+ state: absent
+ when: grocy_bbuddy_enabled is false
+
+ when: grocy_enabled is false
diff --git a/website/docs/applications/other/grocy.md b/website/docs/applications/other/grocy.md
new file mode 100644
index 0000000000..bd550bbdbc
--- /dev/null
+++ b/website/docs/applications/other/grocy.md
@@ -0,0 +1,16 @@
+---
+title: "Grocy"
+---
+
+Homepage:
+
+grocy is a web-based self-hosted groceries & household management solution for your home.
+
+## Usage
+
+Set `grocy_enabled: true` in your `inventories//nas.yml` file. Optionally, set `grocy_bbuddy_enabled: true` to install Barcode Buddy - a barcode system for Grocy.
+
+Set all `grocy_*` variables in `inventories//group_vars/all.yml`.
+
+The grocy web interface can be found at .
+Optionally, Barcode Buddy interface can be found at .