Your SQL results,delivered.
Your SQL results,
delivered.Rowbird runs your queries on a schedule and delivers the results where your team already reads: a table in the email, or CSV, Excel and PDF in Slack, Telegram, Discord, a webhook or a bucket. Add a condition and it becomes an alert.
`$docker run -p 8080:8080 ghcr.io/rowbird/rowbird`Copy
Watch the 21-second tourTry the demoStar on GitHub
sales-by-region.sql
SELECT region, COUNT(*) AS orders, ROUND(SUM(total), 2) AS revenue FROM orders WHERE created_at >= {{yesterday}} GROUP BY region ORDER BY revenue DESC
0 7 \ \ 1-5 · weekdays at 07:00
Daily sales: 5 regionsfrom Rowbird07:00
Daily sales
Good morning! Here are yesterday's sales.
| region | orders | revenue | | ------- | ------ | ------- | | East | 6 | 548.35 | | West | 5 | 500.24 | | South | 8 | 495.87 | | Central | 5 | 382.59 | | North | 1 | 85.10 |
daily-sales.csv.xlsx.pdf
Watch it work.
21 seconds, recorded on a real Rowbird
Fig. 1From a question in plain English to the 07:00 email.
Somebody on your team is a human cron job.
- A cron script only one person understands.
- A BI tool licensed for one table.
- Copy. Paste. Send. Tomorrow again.
- Not a BI tool. No chart builder, no dashboards to maintain.
- Not an ETL tool. It does not move data between databases.
- Not an uptime monitor. It reports to yours, like Uptime Kuma.
- Never a writer. Your data stays exactly as it was.
Rowbird is the small tool that does exactly this job, well, on your own server. It is not a dashboard. It is the report that shows up.
How it works
Three steps, and then it runs without you.
01
Write it, or ask for it.
Write SQL with schema autocomplete and a live preview. Or describe what you need, and the optional assistant (OpenAI, Anthropic, Gemini, Ollama or any compatible endpoint) proposes the query as a change you can read before you apply it.
Supports PostgreSQL · MySQL · MariaDB · SQL Server · SQLite, with TLS and SSH tunnels.

Fig. 2 · A real proposal from the assistant, shown as a diff

Fig. 3 · Weekdays at 07:00, with the next five runs
02
Put it on a schedule.
Cron with a time zone, a visual builder, and the next runs spelled out before you save. 07:00 stays 07:00 across daylight saving. Retries, misfire and overlap policies, and a report that keeps failing pauses itself and tells you why.
0 7 1-5 at 07:00, Monday through Friday
03
Deliver it where people read.
A clean table in the body of the message, files attached, or a download link that expires, needs a sign-in if you want, and keeps a log of who opened it. Files too big for a destination fall back to a link on their own.
CSVExcelJSONPDFHTML tableMarkdownText
Daily sales: 5 regions07:00 · from Rowbird

daily-sales.csvdaily-sales.xlsxdaily-sales.pdf

Fig. 4 · The actual email, with its attachments
Destinations
Wherever your team already reads.
One report can go to several places, each in its own mode. Templates set the subject and the message, with values escaped for each destination.
| Destinations and the delivery modes each supports| Destination | Inline | Files | Link | | --------------------------------------------------------------- | ------------- | ------------- | ------------- | | EmailSMTP, HTML table plus plain text | Supported | Supported | Supported | | SlackBot token or incoming webhook | Supported | Supported | Supported | | TelegramBots, files up to 50 MB | Supported | Supported | Supported | | DiscordWebhooks, Markdown tables | Supported | Supported | Supported | | WebhookJSON, signed with HMAC-SHA256 | Supported | Not supported | Supported | | S3AWS, MinIO, R2, B2, Wasabi, Spaces | Not supported | Supported | Not supported | | Uptime KumaPush monitor, up or down per run | Supported | Not supported | Not supported |
Alerts
Reports that only speak up when it matters.
Add a condition and a report becomes an alert. Combine rules with all or any. When Rowbird itself has a problem, it tells you too: an alert channel with a fallback, grouped messages, a note when things recover, and a heartbeat for your uptime monitor.
1. 01 `has_rows` Only when there is something to say. 2. 02 `row_count > 50` When the list gets long. 3. 03 `revenue < 1000` When a number crosses a line. 4. 04 `changed` Only when the result changed since the last time.
Production access, handled with care
It reads your database. It never writes to it.
Read-only by defaultRead-only transactions, server-side timeouts, row limits and one statement per query. Rowbird warns you when a login can write.
Parameters are bound, alwaysBuilt-in dates like {{yesterday}} and your own parameters reach the database as driver parameters, never pasted into SQL.
Secrets stay secretCredentials are encrypted with AES-256-GCM and never shown again: not in the UI, the API, the logs or an export.
Your people, your rulesAdmin, editor and viewer roles, TOTP and passkeys, OIDC single sign-on, scoped API keys and a security log.
AI that never sees a rowThe optional assistant gets your question and the schema. Rows never leave, and nothing runs until you apply it.
No telemetryRowbird talks to what you configure. The one exception, a daily update check, turns off with a switch.
Config as code
Reports you can review in a pull request.
Export any report as YAML, keep it in Git, and apply it with `rowbird apply`, which prints the plan before it changes anything. Or point Rowbird at a directory and it applies it at every start. Secrets never go in the file.
reports/daily-sales.yaml
apiVersion: rowbird.dev/v1 kind: Report metadata: name: daily-sales spec: query: { ref: sales-by-region } schedule: { cron: "0 7 1-5", timezone: America/Sao_Paulo } condition: { match: all, rules: [{ type: has_rows }] } deliveries: - channel: sales-slack mode: attachment formats: [xlsx, pdf] - channel: finance-email mode: link link: { expiresIn: 7d, requireLogin: true }
What Rowbird is not.
Install
One binary. One minute.
A single Go binary or a small distroless image for Linux, macOS and Windows, on amd64 and arm64. SQLite inside by default, PostgreSQL when you run several instances. Releases are signed and ship with an SBOM.
Docker
Open localhost:8080 and create the first admin.
`$docker run -p 8080:8080 -v rowbird-data:/data ghcr.io/rowbird/rowbird:1`Copy
The demo
Sample data, a fake inbox and four reports, ready to run.
`$git clone https://github.com/rowbird/rowbird && cd rowbird/deploy/demo && docker compose up`Copy
Production
Compose with Caddy and HTTPS, systemd, or Kubernetes.
