What Grocy needs from the host
Grocy tracks stock, best-before dates, shopping lists, recipes, a meal plan, chores, tasks, batteries and household equipment, and its web frontend uses its own REST API for nearly everything. The README calls it a pretty simple PHP application, and the requirements are short:
| Item | From the official README and changelog |
|---|---|
| Runtime | PHP 8.5 with SQLite 3.40+; the 4.6.0 changelog makes it the only supported version |
| PHP extensions | fileinfo, pdo_sqlite, gd, ctype, intl, zlib, mbstring |
| Web server | Document root on public; on nginx add the try_files line, or set DISABLE_URL_REWRITING |
| Storage | A writable data directory; the database is data/grocy.db |
| Clients | A recent Firefox, Chrome or Edge |
The project publishes no CPU, memory or disk minimum, so this page gives none. There is no database daemon to size: SQLite is a file opened by PHP-FPM. The constraint that decides the operating system is the PHP version. Ubuntu 26.04 LTS carries PHP 8.5 in its main archive; older Ubuntu releases package an older PHP, which current Grocy does not support.
Which machine to put it on
One household's Grocy is a single PHP site with one SQLite file. That is the job the Moonshot cartridges on our dedicated server range are described for: one service per machine, the whole cartridge yours, root, a port with no transfer allowance, and month-to-month terms. The micro server is the smallest of them. The same pattern, one PHP or Python app per box with its state in a folder you back up, runs through our Paperless-ngx guide. Pick a rack server instead when Grocy shares the box with other services or you want the out-of-band console, which every racked single-, dual- and quad-socket server has and most cartridges lack (they have power control only).
| Class | Fit for Grocy |
|---|---|
| x86 Moonshot cartridge | Grocy on its own machine |
| Single-socket rack server | Grocy beside other self-hosted services, with a console |
| X-Gene ARM cartridge | Not suitable: Ubuntu 18.04 is the newest system it boots, far short of PHP 8.5 |
Choose an x86 line and select Ubuntu 26.04 on the order form; the systems on offer depend on the line, so check before paying. A racked machine is handed over in about 30 minutes once payment is approved; a built-to-order line takes 4 to 24 hours.
Installing the release zip on Ubuntu 26.04
- Fence the machine first. The
sedline switches ufw's IPv6 handling on before any rule exists, so each rule also covers IPv6; then allow SSH and enable ufw with--forceso the prompt cannot swallow the next pasted line. - Install nginx, PHP-FPM 8.5 and the extensions the README lists.
fileinfo,ctypeandzlibcome with the base PHP package;php -vshould report 8.5. - Unpack the latest release into
/var/www/grocy, copyconfig-dist.phptodata/config.php, and give the web server user write access todataonly. The last lines write an nginx site,/etc/nginx/sites-available/grocy, that listens on loopback only, enable it, and reload nginx after a syntax check.
sudo sed -i 's/^IPV6=.*/IPV6=yes/' /etc/default/ufw
sudo ufw allow 22/tcp
sudo ufw --force enable
sudo apt update
sudo apt install -y nginx php8.5-fpm php8.5-sqlite3 php8.5-gd php8.5-intl php8.5-mbstring unzip wget sqlite3
php -v
sudo mkdir -p /var/www/grocy
cd /var/www/grocy
sudo wget -O grocy.zip https://releases.grocy.info/latest
sudo unzip -q grocy.zip && sudo rm grocy.zip
sudo cp config-dist.php data/config.php
sudo chown -R www-data:www-data /var/www/grocy/data
sudo tee /etc/nginx/sites-available/grocy >/dev/null <<'EOF'
server {
listen 127.0.0.1:8095;
root /var/www/grocy/public;
index index.php;
location / {
try_files $uri /index.php$is_args$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.5-fpm.sock;
}
}
EOF
sudo ln -s /etc/nginx/sites-available/grocy /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Edit data/config.php for currency, locale and the features you use: every major area (chores, batteries, equipment, price tracking and so on) has a feature flag that hides it. Any setting can also come from an environment variable prefixed GROCY_, or a text file in data/settingoverrides.
First login on a loopback port
The README states the default account: user admin, password admin, to be changed immediately. So the site written above listens on 127.0.0.1:8095 only, unreachable from outside even before ufw is considered.
- From your workstation, open a tunnel through your own login on the server:
ssh -L 8095:127.0.0.1:8095 you@server.example.com. - Browse to
http://localhost:8095, sign in as admin/admin, and set a strong password from the user menu at the top right. - Create an account for each person.
DEFAULT_PERMISSIONSinconfig-dist.phpisADMIN, so new users are administrators unless you narrow their permissions or change that setting.
HTTPS, the reverse proxy and barcode scanning
With the password changed, point a DNS record such as grocy.example.com at the server, and in /etc/nginx/sites-available/grocy replace the listen line with listen 80;, listen [::]:80; and a matching server_name. Here nginx is the web server itself, handing PHP to FPM, so no second proxy is needed. Open the ports with sudo ufw allow 80,443/tcp: nginx on the host is an ordinary process, and without that rule ufw blocks it on IPv4 and IPv6 alike. Let's Encrypt supplies the certificate: add the certbot and python3-certbot-nginx packages, then request it with sudo certbot --nginx -d grocy.example.com.
HTTPS is not optional here. The camera scanner decodes barcodes with ZXing, processing the camera stream offline in the browser, and browsers allow it only over a secure connection. The README's author prefers a USB laser scanner, set to prefix each scan with a character such as $ and to send a TAB afterwards. Unknown barcodes are looked up through the bundled Open Food Facts plugin; leaving STOCK_BARCODE_LOOKUP_PLUGIN empty turns external barcode lookups off, so no barcode leaves the server.
Running Grocy under a path rather than its own hostname? Set BASE_PATH and BASE_URL in data/config.php. The settings also offer authentication by a header your proxy sets, or by LDAP, in place of Grocy's own users.
The data folder and backups
Everything Grocy stores is under data: config.php, the SQLite database grocy.db, uploaded files in storage, plus a viewcache it rebuilds itself. Copying grocy.db while PHP is writing can catch it half-written. Either stop PHP-FPM (sudo systemctl stop php8.5-fpm) and copy the folder, or take the copy through SQLite's online .backup command, as the nightly job below does.
sudo mkdir -m 700 -p /var/backups/grocy
sudo tee /usr/local/sbin/grocy-backup >/dev/null <<'EOF'
set -e
umask 077
d=/var/backups/grocy/$(date +%F)
mkdir -p "$d"
sqlite3 /var/www/grocy/data/grocy.db ".backup '$d/grocy.db'"
tar -czf "$d/data.tgz" -C /var/www/grocy --exclude='data/grocy.db*' --exclude=data/viewcache --exclude=data/backups data
EOF
Then schedule it nightly:
echo '20 3 * * * root /bin/bash /usr/local/sbin/grocy-backup > /var/backups/grocy/last-run.log 2>&1' | sudo tee /etc/cron.d/grocy-backup
The job writes one dated folder a night into a root-only directory and logs its last run beside it; it does not prune, so delete old folders when you choose. A backup on the same disk is not a backup: copy /var/backups/grocy to another machine you control. Each folder holds two files, because the database is kept apart from the archive: grocy.db and data.tgz. To restore, say, the night of 2026-10-23:
- Confirm both files exist and are not empty:
sudo test -s /var/backups/grocy/2026-10-23/grocy.db && sudo test -s /var/backups/grocy/2026-10-23/data.tgz && echo ok. Without that ok, stop here and pick another night. - Stop PHP:
sudo systemctl stop php8.5-fpm. - Unpack the archive over the installation:
sudo tar -xzf /var/backups/grocy/2026-10-23/data.tgz -C /var/www/grocy. - Remove any
grocy.db-walorgrocy.db-shmfile left indataby the current database, then copy the saved one in:sudo cp /var/backups/grocy/2026-10-23/grocy.db /var/www/grocy/data/. - Give the folder back to the web server user,
sudo chown -R www-data /var/www/grocy/data, and start PHP withsudo systemctl start php8.5-fpm.
Updating Grocy
The documented update is to overwrite the installation with the latest release and keep data. The bundled update.sh does exactly that and first archives the current installation into data/backups as a .tgz, deleting archives older than 60 days. It also removes everything in its own directory except data and itself, so keep nothing else there. It needs wget and unzip; run it with sudo bash /var/www/grocy/update.sh. Then add one step of our own, which the script does not do: hand data back with sudo chown -R www-data /var/www/grocy/data. The script runs as root, so the files it writes come out owned by root, and PHP-FPM could no longer write to them.
- Read the release's changelog first. The 4.6.0 release moved the runtime to PHP 8.5; 4.7.0 reorganised authentication and asked admins to review
AUTH_CLASS. - Compare
config-dist.phpwith yourdata/config.php; settings you have not defined take the defaults. - The database schema is migrated automatically after a version change, or whenever the root page is opened. Those migrations are designed to work between releases, not between commits on the
masterbranch.
Grocy in Docker
Grocy has no official container image. The README's Docker pointer leads to linuxserver/grocy, and grocy.info lists that image as a community contribution, so it is built and documented by LinuxServer.io rather than the Grocy project. Its own documentation names the ports and volumes.
If you choose it, Docker changes the firewall picture. Leave the address off a port mapping and Docker listens on every host address, IPv4 0.0.0.0 and IPv6 [::]. Grocy's port would then slip past every ufw rule over IPv4: Docker redirects that traffic to the container and it is forwarded, never crossing the INPUT chain where ufw's rules sit. Over IPv6 the same happens if the daemon manages IPv6 itself; if it does not, docker-proxy holds the IPv6 socket and ufw filters that process normally. So write the mapping as 127.0.0.1 plus the port, and let the host's nginx face the internet. Docker Engine 28 and newer keep such a loopback mapping local to the machine. Back up the volume holding its data with the same care.
Questions
What are the Grocy login details after a fresh install?
The README gives user admin with password admin. Change the password from the user menu straight away, and do it over an SSH tunnel to a loopback-bound port before the site has a public address.
Is Grocy free software, and under which licence?
Yes. The repository's licence file is the MIT License, copyright Bernd Bestel, who runs Grocy as a hobby project. Support is through the GitHub issue tracker and the r/grocy subreddit, not private messages.
Can I run Grocy on PHP 8.3 or 8.4?
Not on current releases. The 4.6.0 changelog says PHP 8.5 with SQLite 3.40 or newer is from then on the only supported runtime, and the release's composer.json asks for 8.5. Install it on a system that packages PHP 8.5, such as Ubuntu 26.04.
Is there a Grocy app for phones?
The web frontend is responsive and installable as a progressive web app, though without offline use. grocy.info also lists Grocy Android and Grocy Mobile for Apple devices, both community contributions rather than part of the Grocy project.
Where does Grocy keep its database?
In data/grocy.db, an SQLite file next to config.php and the storage folder of uploads. A backup should copy all of data, taking the database with SQLite's .backup command or with PHP-FPM stopped.
Does Grocy need a big server?
Grocy states no CPU or memory floor, and there is no separate database service: SQLite runs inside PHP. What matters is a system with PHP 8.5, which rules out machines limited to old Ubuntu releases.