ATProto Feed Generator powered by The AT Protocol SDK for Python
Feed Generators are services that provide custom algorithms to users through the AT Protocol.
Official overview (read it first): https://github.com/bluesky-social/feed-generator#overview
We've set up this server with libSQL to store and query data. This supports local database files as well as remote/embedded replicas using Turso.
Next, you will need to do three things:
- Implement filtering logic in
server/data_filter.py. - Copy
.env.exampleto.envand fill in your settings. - Optionally implement custom feed generation logic in
server/algos.
We've taken care of setting this server up with a did:web. However, you're free to switch this out for did:plc if you like - you may want to if you expect this Feed Generator to be long-standing and possibly migrating domains.
To publish your feed, simply run:
uv run publishTo update your feed's display data (name, avatar, description, etc.), just update the relevant variables in .env and re-run the script.
After successfully running the script, you should be able to see your feed from within the app, as well as share it by embedding a link in a post (similar to a quote post).
This project requires Python 3.14+ and is configured to use the uv toolchain for dependency management.
Run uv sync to setup a virtual environment and install the dependencies:
uv syncNote: To get a value for FEED_URI, you need to publish the feed first.
To run the development Flask server:
uv run devTo run the production-grade Gunicorn WSGI server:
uv run prodWarning: If you want to run the server with many workers, you should run the Data Stream (Firehose) separately.
/.well-known/did.json/xrpc/app.bsky.feed.describeFeedGenerator/xrpc/app.bsky.feed.getFeedSkeleton
This project includes configuration for automated container builds and rootless deployment using Podman Quadlets.
The GitHub workflow .github/workflows/build-image.yml builds and pushes the container image to the GitHub Container Registry (GHCR) at ghcr.io/<username>/bluesky-feed-generator:latest.
- Triggers: It runs automatically on push to the
mainbranch, when a new tagv*is pushed, and on any pull requests targetingmain. - Registry Permissions: Ensure your workflow has permissions to write packages in your repository settings.
Quadlet is the modern, recommended way to run rootless Podman containers managed by systemd.
- Enable lingering for your user so that the systemd services can start on system boot without you logging in:
loginctl enable-linger $USER - Create the configuration and data persistence directories:
mkdir -p ~/.config/bluesky-feed-generator mkdir -p ~/.local/share/bluesky-feed-generator/data
- Copy your
.envconfiguration file to~/.config/bluesky-feed-generator/env:Make sure you setcp .env ~/.config/bluesky-feed-generator/envLIBSQL_URL=/app/data/feed_database.dbinside this environment file so that the SQLite/LibSQL database is stored on the persistent host mount.
-
Copy the Quadlet container file to your user's systemd directory:
mkdir -p ~/.config/containers/systemd cp podman/bluesky-feed-generator.container ~/.config/containers/systemd/
Update the
Image=option inside the copied file to point to your specific GHCR repository if needed. -
Reload the systemd daemon to generate the transient service files:
systemctl --user daemon-reload
-
Start and enable the service:
systemctl --user enable --now bluesky-feed-generator.service -
Check the service status and logs:
systemctl --user status bluesky-feed-generator.service journalctl --user -xeu bluesky-feed-generator.service
Because AutoUpdate=registry is set in the container file, you can configure Podman to check for newer images on GHCR and automatically restart the service:
systemctl --user enable --now podman-auto-update.timerMIT