Mezzo is an open-source, privacy-focused front-end for Tenor that allows you to browse and view GIFs without Google's tracking and advertising systems.
For public instances of Mezzo, see mezzo-instances.
We have pre-built images you can run.
To run the latest stable version of Mezzo (recommended):
docker run -p8006:8006 gitfield.org/fsky/mezzo:latestTo run the canary (unstable) version of Mezzo built from the latest Git commit:
docker run -p8006:8006 gitfield.org/fsky/mezzo:canaryIf you are using Podman, the process is the same. Just replace docker with podman in the command.
You can find a compose file in contrib/compose/compose.yaml. Simply download it and run:
docker compose up -d
You can find a Quadlet file in contrib/quadlet/mezzo.container. Download it and place it into ~/.config/containers/systemd/ or /etc/containers/systemd/. After that, run:
systemctl --user daemon-reload
systemctl --user start mezzo.service
Binaries are provided for Linux, Windows, macOS, FreeBSD, OpenBSD, NetBSD, and Illumos for amd64, arm64, and i386. You can download them from the releases page.
Once downloaded, run the binary directly. On Unix-like systems you may need to mark it as executable first:
chmod +x mezzoLinux packages are also published for Debian/Ubuntu (.deb), RPM-based distributions (.rpm), and Arch Linux (.pkg.tar.zst).
The packages install:
- the
mezzobinary into/usr/bin - a systemd service unit
- a distro-appropriate environment file:
/etc/default/mezzoon Debian/etc/sysconfig/mezzoon RPM-based systems/etc/conf.d/mezzoon Arch Linux
After installation, edit the environment file if needed, then enable the service:
sudo systemctl enable --now mezzoFor systemd, you can find an example service file in contrib/systemd/mezzo.service.
For OpenRC, you can find an example service script and configuration file in contrib/openrc/.
This program is written in Go. You need Go 1.25 or later.
You can build a binary using:
go build -o mezzo ./cmd/mezzoAn optional AppArmor profile is in packaging/apparmor/usr.bin.mezzo. It lets Mezzo listen on a TCP port, resolve DNS, make HTTPS requests, and read and write its media cache directory. It does not let Mezzo run other programs or touch any other file. It has been tested on Ubuntu 24.04 and Debian 12.
To install it on Debian or Ubuntu and restart the service:
sudo install -m 644 packaging/apparmor/usr.bin.mezzo /etc/apparmor.d/usr.bin.mezzo
sudo apparmor_parser -r /etc/apparmor.d/usr.bin.mezzo
sudo systemctl restart mezzoThe profile applies to /usr/bin/mezzo and /usr/local/bin/mezzo, and allows the media cache in /var/cache/mezzo, where the systemd unit keeps it. If you use a different MEZZO_CACHE_MEDIA_DIR or a port below 1024, add rules to /etc/apparmor.d/local/usr.bin.mezzo, which is kept across upgrades, then reload the profile with the apparmor_parser -r command above:
# a different MEZZO_CACHE_MEDIA_DIR
/srv/mezzo-cache/ r,
/srv/mezzo-cache/** rw,
# a listening port below 1024 (the service needs the capability itself too)
capability net_bind_service,
If you delete the local file later, reload with sudo apparmor_parser -r -T /etc/apparmor.d/usr.bin.mezzo. Otherwise AppArmor may keep using a cached copy of the profile that still has the old rules.
To see what the profile would block without enforcing it, load it in complain mode with sudo apparmor_parser -r -C /etc/apparmor.d/usr.bin.mezzo. The kernel logs these accesses (journalctl -k | grep apparmor, or /var/log/audit/audit.log if auditd is installed). Without auditd the kernel may drop some of the messages.
For a container, load the profile the same way, start the container with --security-opt apparmor=mezzo, and allow the container's cache path (/cache in the compose and Quadlet files) in the local file. This was tested with rootful Podman, including with no-new-privileges.
The application can be configured using the following environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
MEZZO_PORT |
No | 8006 |
Port to run on. |
MEZZO_PATCHES_URL |
No | Link to any patches that were applied. Only set this if your instance runs modified source code. | |
MEZZO_CACHE_DISABLED |
No | false |
Disable all caching, including the media cache on disk. |
MEZZO_CACHE_GIF_TTL |
No | 1h |
How long to cache GIF page metadata. Accepts Go duration strings (e.g. 30m, 2h). |
MEZZO_CACHE_GIF_MAX |
No | 1000 |
Maximum number of GIF pages to keep in cache. |
MEZZO_CACHE_SEARCH_TTL |
No | 10m |
How long to cache search results, Explore page results, later pages of a profile's GIFs, stickers and memes, and later pages of related GIFs. Accepts Go duration strings. |
MEZZO_CACHE_SEARCH_MAX |
No | 500 |
Maximum number of search result pages (and, separately, Explore pages) to keep in cache. Later profile pages and later pages of related GIFs share the search result slots. |
MEZZO_CACHE_PROFILE_TTL |
No | 30m |
How long to cache user profile pages. Accepts Go duration strings. |
MEZZO_CACHE_PROFILE_MAX |
No | 200 |
Maximum number of user profiles to keep in cache. |
MEZZO_CACHE_MEDIA_DIR |
No | Directory for the media cache on disk. The cache is off unless this is set. | |
MEZZO_CACHE_MEDIA_MAX_MB |
No | 512 |
Maximum total size of the media cache, in MiB. |
MEZZO_CACHE_MEDIA_MAX_FILE_MB |
No | 16 |
Largest single file to store in the media cache, in MiB. |
MEZZO_CACHE_MEDIA_TTL |
No | 168h |
How long to keep GIFs, videos and thumbnails in the media cache. Accepts Go duration strings, which have no unit for days. |
MEZZO_CACHE_AVATAR_TTL |
No | 24h |
How long to keep avatars and banners in the media cache and in browsers. Accepts Go duration strings. |
Note
For backward compatibility, PORT and PATCHES_URL (without the MEZZO_ prefix) are still accepted as fallbacks.
A variable that is set but cannot be parsed (for example MEZZO_CACHE_GIF_TTL=1hour) stops Mezzo from starting, and the error names every such variable. Unset or empty variables use the default.
Out of the box, Mezzo fetches every GIF, video, thumbnail and avatar from Tenor each time someone asks for it. Set MEZZO_CACHE_MEDIA_DIR to a directory Mezzo can write to and it keeps a copy there instead, so repeat views load faster and Tenor sees fewer requests from your instance.
- Use a directory that nothing else uses. Mezzo only ever deletes the files it created there.
- The cache never grows past
MEZZO_CACHE_MEDIA_MAX_MB. When it is full, the least recently used files are deleted first. Files larger thanMEZZO_CACHE_MEDIA_MAX_FILE_MBare passed through without being stored. - GIFs, videos and thumbnails are kept for
MEZZO_CACHE_MEDIA_TTL. Avatars and banners can change, so they are kept forMEZZO_CACHE_AVATAR_TTLinstead, which is also how long browsers keep them. - Responses to media requests have a
Mezzo-Cacheheader that says whether the file was served from the cache (HIT), fetched and stored (MISS), or fetched but not stored (BYPASS). - The directory is a record of what has been viewed on your instance, like an access log. Keep it private, and keep the TTLs short if that matters to your users.
MEZZO_CACHE_DISABLED=trueturns the media cache off too.
To enable it with the packages or the systemd unit, uncomment MEZZO_CACHE_MEDIA_DIR in the environment file; the unit provides /var/cache/mezzo. With containers, see the comments in the compose file and the Quadlet file.
Copyright © 2026-present FSKY. This software is distributed under the AGPL-3.0 license.



