Free, self-hosted showing feedback collector for real estate agents. When an agent shows a property, you send them a magic link — they fill out a quick rating and comment form, and you see the feedback on your dashboard. Sellers get an anonymous report they can view anytime.
No signup. No license. One Docker Compose service and a SQLite file. About 10 minutes on a 1GB VPS.
- Create listings with seller details, get a unique anonymous seller report link
- Add showings with agent name/email/phone — each showing gets its own feedback magic link
- Agents click the feedback link (no login, no app install) — rate interest 1–5 and condition 1–5, add price opinion, comments
- Dashboard shows response rates, average scores, and per-showing details
- Seller report (anonymous, no agent names) with aggregate stats and per-response comments
- Toggle individual responses on/off in the seller report
- Import showings from a CSV file (sample file included)
- Export showing data (including feedback status) as CSV
- Archive listings to keep the dashboard clean
- Fully self-hosted — data stays on your server
- Not Nudge — no automated follow-up drips
- Not OpenPing — no tracked document/quote links
- Not FormFirst — no contact-form auto-reply
- Not a CRM, MLS integration, or transaction management system
Self-hosted. You run the box; you control the data. Agent names and contact details are stored in your SQLite database and never sent anywhere. The seller report is anonymized (agent names are hidden). No third-party analytics, no telemetry, no license server.
Documented on Ubuntu 22.04 / 24.04. About 10 minutes.
Debian 13: do not run the Ubuntu docker-ce recipe below on Debian. Use the distro packages instead:
sudo apt-get update
sudo apt-get install -y docker.io docker-compose
sudo usermod -aG docker "$USER"Log out and back in (or newgrp docker). On Debian, start the stack with docker-compose (hyphen) if docker compose is not available.
Amazon Linux: not documented yet. Use Ubuntu or Debian.
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo ${UBUNTU_CODENAME:-$VERSION_CODENAME}) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo usermod -aG docker "$USER"Log out and back in (or run newgrp docker) so docker works without sudo.
git clone https://github.com/aidendify/showfeed.git
cd showfeed
docker compose up --build -d(On Debian, docker-compose up --build -d if the Compose plugin is not installed.)
The app binds to host port 8080. To use a different port, set HOST_PORT:
HOST_PORT=18080 docker compose up --build -dNo .env file is required to start — all defaults work out of the box.
Open http://YOUR_SERVER_IP:8080 in your browser.
The first visit shows a Create Admin Password screen. Enter a password (at least 8 characters) and confirm it. You are then logged in and ready to go.
The first time you open ShowFeed, you'll see a Create Admin Password screen. Enter a password (at least 8 characters), confirm it, and click Create. You are then logged in and ready to go. You can change the password later in Settings.
- Open your ShowFeed dashboard at http://YOUR_SERVER_IP:8080 (or http://localhost:8080 during testing)
- Click Import CSV in the navigation bar
- Click Download sample-showings.csv to download the bundled sample file to your computer
- Click Choose File, select the downloaded
sample-showings.csv, and click Upload and Import - You are redirected to the dashboard — you should see two listing cards:
- 123 Main St, Anytown
- 456 Oak Ave, Anytown
- Click on 123 Main St, Anytown to see the detail page
- You'll see the stats grid: Showings: 4, Responses: 0, Response Rate: 0.0%
- Below the stats grid, you'll see the Feedback Links section with 4 entries:
- Alice Buyer — Pending —
/f/...— Copy — Draft Email - Bob Buyer — Pending —
/f/...— Copy — Draft Email - Carol Buyer — Pending —
/f/...— Copy — Draft Email - Dan Buyer — Pending —
/f/...— Copy — Draft Email
- Alice Buyer — Pending —
- Click the Copy button next to Alice Buyer (first row). The button changes to "Copied!"
- Open a new private/incognito browser tab and paste the copied feedback link (the
/f/...URL) - Rate Interest: 4, Condition: 3, Price opinion: about right, Likely to offer: yes
- Click Submit Feedback — you see a Thank You page with "Feedback received. Your responses have been recorded."
- Scroll down to the Responses table on the listing detail page. Each row shows the buyer agent's name, their feedback (Liked, Concerns), a Private note column (visible only to you), and the toggle state — Shown to seller or Hidden from seller. Click Hide from seller to remove that response's detail from the seller report.
- Click Seller Report — this is the shareable page with aggregate stats and responses you've chosen to show (hidden responses are still counted in stats).
- Click Copy next to the shareable link — this
/s/...URL is what you send to the homeowner - Open the seller link in a private tab — the report shows anonymized stats without agent names (buyer's agents' names, emails, phone numbers and private notes are never shown to the seller)
- Click New Listing and fill in the address, price, MLS number, and seller details
- On the listing detail page, click Add Showing and enter the agent name, date, and time
- The seller report link is per listing and visible on the Seller Report page. Each showing's feedback can be toggled on/off in the report.
All configuration is optional. The app works without a .env file.
| Variable | Default | Purpose |
|---|---|---|
HOST_PORT |
8080 | Host port mapped to container (set via docker compose, not inside container) |
DATABASE_PATH |
/data/showfeed.db | SQLite database file location |
SECRET_KEY |
Auto-generated, persisted to /data/secret_key | Flask session signing key. Auto-generated on first run if not set |
ADMIN_PASSWORD |
(none) | Optional env variable. If set, takes precedence over the setup-page password — use this to recover access if you forget your password. See Troubleshooting below. |
BUSINESS_NAME |
ShowFeed | Name shown in the site header and seller report footer |
AGENT_NAME |
(empty) | Your name as the listing agent, shown on public pages (feedback form, seller report) and in email signatures. It does NOT pre-fill the buyer's agent name. |
PUBLIC_BASE_URL |
(auto-detected) | Public URL for magic links. Set this to http://YOUR_SERVER_IP:8080 or your domain so emailed/copied links work for other people. When unset and accessed via localhost, a yellow notice appears on the listing page warning that links are local-only. |
TIMEZONE |
UTC | IANA timezone name (e.g. America/New_York, Europe/London) for displaying stored UTC timestamps. Invalid values log a warning and fall back to UTC. Showing dates/times entered by the user are not converted. |
SMTP_HOST |
(empty) | Optional SMTP server hostname. When set, a "Send request email" button appears for showings that have an agent email. |
SMTP_PORT |
587 | SMTP server port |
SMTP_USER |
(empty) | Optional SMTP username for authentication |
SMTP_PASSWORD |
(empty) | Optional SMTP password for authentication |
SMTP_FROM |
(required when SMTP_HOST is set) | From address for sent emails |
SMTP_USE_TLS |
true | Whether to use STARTTLS (true, false). Defaults to true |
docker compose run --rm web python -m pytest -qThe run ends with passed in the output and no failed.
This stack requires Docker Compose v2.24 or newer (needed for the optional .env file support). Check your version with docker compose version.
Go to Settings in the navigation bar to change your business name, agent name, and password. These appear on public pages and email drafts.
For advanced configuration (PUBLIC_BASE_URL, SMTP, etc.), create a .env file from the example:
cp .env.example .env
nano .envAfter editing, restart the container:
docker compose up -dWhen you need PUBLIC_BASE_URL: Set this when using ShowFeed behind a reverse proxy or domain. For example, if your server is at https://showfeed.example.com, set PUBLIC_BASE_URL=https://showfeed.example.com. The feedback and seller links on the listing page use this URL so recipients can access them. When unset and accessed via localhost, a yellow notice appears showing that links contain localhost and may not work for others.
The entire application data lives inside the container at /data/. To back it up without knowing the volume name:
docker compose exec web sh -c "tar czf /tmp/showfeed-backup.tar.gz -C /data ."
docker compose cp web:/tmp/showfeed-backup.tar.gz ./showfeed-backup-$(date +%F).tar.gzRestore:
docker compose cp ./showfeed-backup-2026-10-01.tar.gz web:/tmp/showfeed-restore.tar.gz
docker compose exec web sh -c "tar xzf /tmp/showfeed-restore.tar.gz -C /data"
docker compose restartcd showfeed
git pull
docker compose up --build -dSQLite schema migrations run automatically on startup.
cd showfeed
docker compose downTo also remove the database (irreversible — back up first):
docker compose down -vThe app doesn't start — "port already in use" Another process is using port 8080. Use a different host port:
HOST_PORT=18080 docker compose up -d"Internal Server Error" on first visit
Make sure the showfeed-data volume exists and the /data directory is writable. On some systems, SELinux or AppArmor blocks container writes. Check the logs:
docker compose logsImport says "file too large" The CSV upload limit is 2 MB. For larger datasets, split the file.
Feedback link shows "Invalid or expired link" The showing may have been deleted. Check the listing detail page to confirm the showing still exists.
"Not Found" when visiting the seller link The seller token may have been regenerated. Ask the agent for the new link.
Forgot your password? If you've lost access, you can reset it using an environment variable:
- Create a
.envfile if you don't have one:cp .env.example .env - Add or edit the line:
ADMIN_PASSWORD=your-new-password - Restart the container:
docker compose up -d - Log in with the password you set in
.env - Go to Settings and change your password using the "Change Password" form
- Remove the
ADMIN_PASSWORDline from.env(or comment it out) - Restart again:
docker compose up -d
The ADMIN_PASSWORD environment variable always takes precedence over the password stored in the database, so you can use it to regain access even if you've forgotten your original password.