Fredy watches 29 property portals for you, and signs you in as admin
❤️ Fredy - [F]ind [R]eal [E]state [D]amn Eas[y] - Fredy keeps searching for new apartments, houses, and flats in Europe on platforms like ImmoScout24, Immowelt, eBay Kleinanzeigen and instantly delivers the results to you via Slack, Telegram, Email, Discord or ntfy, so you can focus on the more important things in life ;)
At a glance
- What is it?
- Fredy is an Apache-2.0 self-hosted scraper and notifier that reads 29 European property portals, drops duplicates across them and pushes new listings to Slack, Telegram, ntfy and more. Its own files are more informative than its marketing: the Docker base image choice is explained by a glibc error message, the font list by a bot defence, and the first run hands you admin / admin.
- Who is it for?
- Fredy suits a renter or buyer in one of its seven covered countries who is willing to run a browser inside a container and accept that portals will eventually change underneath it. Before the first start, change the default admin credentials, decide between bind mounts and named volumes so your data lives somewhere you chose, and pin a release tag rather than tracking master.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 2, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A first run signs you in as admin, so the first job is rotating it
The quick start ends with one instruction: open http://localhost:9998 and sign in with admin and admin. No secret is passed to the container, and the note underneath explains why. No configuration file is needed to start, because /conf/config.json is created on first run if it is missing, and that file holds only the database path while everything else is configured in the Web UI and stored in the database. So the first credential is a shipped default rather than a generated one. The compose file reinforces the shape by setting NODE_ENV=production and nothing else in its environment block, with no secrets file referenced. This matters more here than it would for a plain dashboard, because every notification channel holds a bot token, an SMTP password or a webhook URL in that same database. Relatedly, documents you attach to a listing are stored in the database rather than in a media folder, so the /db volume keeps them and any backup already contains them, and a listing with documents is never cleaned up automatically.
A channel is one saved destination, which is why rotating a token is a single edit
The model is four nouns. A provider is a property platform, and you paste a search URL from the portal into Fredy, sorted by date. An adapter is a kind of connection Fredy can send through, and you never configure one on its own. A channel is one filled-in adapter, for example Telegram pointed at a family chat, saved once under Settings and Notification channels. A job is providers plus channels, such as search ImmoScout24 and Immowelt and send to Slack and Telegram, and it runs at the interval set under Administration and Execution. A job can hold as many channels as you want, including several of the same type, and every new listing goes out through all of them at once. That is what makes rotating a token one edit instead of an edit in every job. Sharing is handled without leaking secrets: an admin can share a channel with all users or with admins only, letting others send through it without ever revealing its credentials. One constraint cuts the other way, since a channel still used by a job cannot be deleted.
The arm64 image needs Debian 13 because of one prebuilt SQLite binary
The Dockerfile opens with a comment explaining the base image, and the failure it prevents is specific. Since better-sqlite3 v13, the npm tarball ships prebuilt binaries, and prebuilds/linux-arm64.node is linked against GLIBC_2.38. Debian 12 bookworm only has glibc 2.36, so arm64 containers crashed on startup with libm.so.6: version `GLIBC_2.38' not found. That is why the image is node:22-trixie-slim rather than a bookworm tag. The same comment block warns that trixie renamed several libraries as part of the 64-bit time_t transition, so the apt list deliberately keeps the t64 suffixes on libasound2t64, libatk-bridge2.0-0t64 and libcups2t64. If you build this on an older base yourself, this is the first error you meet, and the fix is the base image rather than a compiler flag.
The image installs fonts because bot defences hash hidden canvases
The apt-get line is longer than a Node service needs, and the Dockerfile says why. CloakBrowser is the browser used for fetching listings, and sites like Kasada or Akamai render emoji and CJK glyphs on hidden canvases and hash the pixel output, so a missing font set produces hashes that a minimal Linux image cannot match. The package list answers with fonts-liberation, fonts-noto-color-emoji, fonts-freefont-ttf, fonts-unifont, fonts-ipafont-gothic, fonts-wqy-zenhei and fonts-tlwg-loma-otf, alongside GTK, DRM and NSS libraries and python3, make and g++ for native modules. The comment also draws a line it cannot cross: real Windows fonts such as Segoe UI or Calibri cannot be bundled, since that would mean copying licensed files off a Windows installation, and the startup notice produced by the font warning suppression variable is therefore expected. Every log will carry that notice, and it is not a symptom worth chasing.
yarn test reaches live portals, and yarn test:offline is the one that is repeatable
The scripts in package.json split testing in a way worth knowing before you run anything. The test script sets TEST_MODE=live and runs vitest run, while test:offline sets TEST_MODE=offline for the same runner, and a third script, test:download-fixtures, fetches fixture files with node tools/testFixtures/downloadFixtures.js. The default command therefore contacts real portals, and the offline suite is the one meant for a repeatable run. That split shows up in the release process too: the pre-release image is built from the same pipeline, where lint, format check and the offline test suite all have to pass first, and only then are the changes published without going through master. Tooling is oxlint via .oxlintrc.json and oxfmt via .oxfmtrc.json, lint-staged runs lint, format and a copyright script on staged js and jsx files, and husky is installed by the prepare script. Vite is pinned exactly to 8.3.0 in resolutions. The same listings are also exposed to Claude, ChatGPT or a local model through a stdio MCP server started by the mcp:stdio script.
Three reverse engineering notes sit at the repository root
Three markdown files sit next to the README: reverse-engineered-immoscout.md, reverse-engineered-idealista.md and reverse-engineered-casa.md. The feature list states that the project uses the reverse engineered ImmoScout Mobile API, so those notes are the record of how the endpoints were recovered, kept in the repository rather than hidden in a private wiki. The same candour shows up in deduplication, the feature that suffers first when it is advertised loosely. The same flat advertised on ImmoScout, Immowelt and Kleinanzeigen reaches you once, matched on living space, rooms and location rather than on the headline, because no two portals write the same listing the same way. The scope is 29 portals across seven countries, with ImmoScout24 covering both Germany and Austria, and the named set also includes Immowelt, Kleinanzeigen, WG-Gesucht, willhaben, Flatfox, idealista, Subito, leboncoin and SeLoger.
The compose file builds locally and caps memory, the short run command does neither
Two deployment shapes exist and they are not the same thing. The compose service sets container_name: fredy, builds from the local context with the repository Dockerfile, and also names an image without a tag, while the quick start pulls a published one:
docker run -d --name fredy \
-v fredy_conf:/conf \
-v fredy_db:/db \
-p 9998:9998 \
ghcr.io/orangecoding/fredy:masterVolume handling differs as well: named volumes in the run command, bind mounts of ./conf and ./db in compose. Compose also adds what the run command leaves out, a one gigabyte memory limit whose comment calls it protection against runaway Chromium usage, and a healthcheck that curls localhost:9998 with a five second limit every two minutes, with a thirty second start period and three retries. Published images are built for linux/amd64 and linux/arm64, so the architecture is not the reason to build locally.
Tag choice matters because master moves and a pre-release channel exists
Docker is the recommended path and the running container is read with docker logs fredy -f. The source path asks for Node.js 22.22.0 or higher and splits the build:
yarn
yarn run build:frontend # builds the Web UI into ui/public
yarn run start:backend # serves the UI and the API on port 9998A third option is the Unraid community store, which is the short path for anyone already running that platform. The Web UI itself is available in several languages and carries configurable search intervals and working hours, so a scraper can stay quiet at night. Tags are the part to think about before anything else: the master tag follows the master branch, and the documentation suggests pinning a release tag such as ghcr.io/orangecoding/fredy:28.0.0 instead. A separate pre-release channel, ghcr.io/orangecoding/fredy-pre-release:latest, follows the develop branch and is meant for trying features or verifying a fix, not for an instance you rely on. Release numbering moved through 29.1.1, 29.2.0 and 29.2.1 in the last week of September 2026.
Editorial conclusion
Fredy suits a renter or buyer in one of its seven covered countries who is willing to run a browser inside a container and accept that portals will eventually change underneath it. Before the first start, change the default admin credentials, decide between bind mounts and named volumes so your data lives somewhere you chose, and pin a release tag rather than tracking master. Read the compose file for the memory cap and healthcheck, because the short docker run command has neither, and keep an eye on reverse-engineered-immoscout.md when a portal starts returning nothing.
Frequently asked questions
What are the default Fredy login credentials?
admin and admin, straight after the first start. The quick start points at http://localhost:9998 and names those two values, and no configuration file has to be prepared because /conf/config.json is created on first run and holds only the database path.
How does Fredy avoid sending the same flat twice?
Duplicates across platforms are matched on living space, rooms and location rather than on the headline, because no two portals write the same listing the same way. A flat advertised on ImmoScout, Immowelt and Kleinanzeigen therefore reaches you once.
Which Fredy container image should an operator run?
The master tag follows the master branch and keeps moving, so pin a release tag such as ghcr.io/orangecoding/fredy:28.0.0. A pre-release channel at ghcr.io/orangecoding/fredy-pre-release:latest follows develop and is meant for trying features or verifying a fix rather than for a relied upon instance.
Can other people send through a Fredy notification channel without seeing the token?
Yes. A channel belongs to whoever created it, and an admin can share one with all users or with admins only, which lets others send through it without ever revealing its credentials. A channel still used by a job cannot be deleted.
Where does Fredy keep documents attached to a listing?
In the database rather than in a media folder, so the /db volume is all you need to keep them and every backup already contains them. A listing that has documents attached is never cleaned up automatically.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/orangecoding-fredy)