惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
博客园 - 司徒正美
WordPress大学
WordPress大学
爱范儿
爱范儿
小众软件
小众软件
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
罗磊的独立博客
博客园_首页
V
V2EX
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Tailwind CSS Blog
大猫的无限游戏
大猫的无限游戏
The Cloudflare Blog
MyScale Blog
MyScale Blog
IT之家
IT之家
H
Help Net Security
Blog — PlanetScale
Blog — PlanetScale
Microsoft Security Blog
Microsoft Security Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Recent Announcements
Recent Announcements
F
Fortinet All Blogs
The GitHub Blog
The GitHub Blog
Y
Y Combinator Blog
人人都是产品经理
人人都是产品经理

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Build a daily email digest with cron and 6 lines of bash
Qasim Muhamm · 2026-05-05 · via DEV Community
Cover image for Build a daily email digest with cron and 6 lines of bash

Qasim Muhammad

Every product team rebuilds the same thing: a 7 AM email summarising what happened yesterday. Signups. Errors. New customers. Pipeline counts. Some teams use Looker. Some pay Zapier. Some have a sheet that nobody opens.

The honest answer for most cases: 6 lines of bash, one cron entry, ship.

What you need

  • A Linux box (or macOS) with cron
  • Whatever query speaks to your data (psql, mysql, an API)
  • The Nylas CLI installed and authenticated
brew install nylas/nylas-cli/nylas
# or: curl -fsSL https://cli.nylas.com/install.sh | bash
nylas auth config --api-key YOUR_KEY

Enter fullscreen mode Exit fullscreen mode

The script

Save as /opt/scripts/digest.sh:

#!/usr/bin/env bash
set -euo pipefail

SIGNUPS=$(psql -tA -h db.example.com -U readonly app -c "SELECT count(*) FROM users WHERE created_at > now() - interval '1 day'")
ERRORS=$(psql -tA -h db.example.com -U readonly app -c "SELECT count(*) FROM events WHERE level='error' AND created_at > now() - interval '1 day'")
REVENUE=$(psql -tA -h db.example.com -U readonly app -c "SELECT coalesce(sum(amount_cents), 0)/100 FROM payments WHERE created_at > now() - interval '1 day'")

nylas email send --to team@yourapp.com \
  --subject "Daily digest $(date +%F)" \
  --body "Signups: $SIGNUPS\nErrors: $ERRORS\nRevenue: \$$REVENUE"

Enter fullscreen mode Exit fullscreen mode

That is it. Six lines if you count the shebang and the set directive.

The cron entry

# /etc/cron.d/digest
0 7 * * 1-5 ops bash /opt/scripts/digest.sh

Enter fullscreen mode Exit fullscreen mode

7 AM, Monday through Friday, run as the ops user. Logs to syslog by default — journalctl -u cron to inspect.

Why this beats the alternatives

Alternative Cost Flex
Looker / Metabase scheduled email $50+/seat/mo Drag-drop dashboards, no script
Zapier scheduled task $0.024 per run = $0.50/mo per digest Visual editor, vendor lock
Internal "ReportingService" 4 weeks of eng time Future-proof
This script Whatever Postgres + email cost Total

For 90% of "email me yesterday's numbers" requests, the script wins. The other 10% need charts, drill-down, or non-engineering authoring — that is when Looker pays back.

Make it nicer

HTML body for charts

nylas email send --to team@yourapp.com \
  --subject "Daily digest $(date +%F)" \
  --html "<h1>Daily digest</h1><table><tr><th>Metric</th><th>Value</th></tr><tr><td>Signups</td><td>$SIGNUPS</td></tr></table>"

Enter fullscreen mode Exit fullscreen mode

Sparklines from the last 7 days

SPARK=$(psql -tA -c "SELECT array_agg(c) FROM (SELECT count(*) c FROM users WHERE created_at > now() - interval '7 days' GROUP BY date_trunc('day', created_at) ORDER BY 1) t" | sed 's/[{}]//g')
# Render with https://github.com/holman/spark
SPARK_LINE=$(echo $SPARK | tr ',' ' ' | spark)

Enter fullscreen mode Exit fullscreen mode

Attachment with the raw CSV

psql -c "\copy (SELECT * FROM yesterday_summary) TO '/tmp/digest.csv' CSV HEADER"
nylas email send --to team@yourapp.com \
  --subject "Daily digest $(date +%F)" \
  --body "Numbers attached." \
  --attachment /tmp/digest.csv

Enter fullscreen mode Exit fullscreen mode

Skip weekends or holidays

# At top of digest.sh:
HOLIDAYS_FILE=/opt/scripts/us-holidays.txt
TODAY=$(date +%F)
if grep -qx "$TODAY" "$HOLIDAYS_FILE"; then
  exit 0
fi

Enter fullscreen mode Exit fullscreen mode

A common gotcha

Cron runs with a minimal PATH. If nylas is in ~/.config/nylas/bin (the default install location), either symlink it to /usr/local/bin/nylas or set PATH at the top of your script:

export PATH="$HOME/.config/nylas/bin:$PATH"

Enter fullscreen mode Exit fullscreen mode

If your job runs as a different user (e.g., ops), the install needs to be visible to that user.

Going further

  • Per-team digests: loop over a list of recipients with for team in eng product sales; do nylas email send ...; done
  • On-call alerts: combine with a service that emits errors to a queue, send only when count > threshold
  • Daily metrics from Coralogix / Datadog: replace psql with the provider's CLI or curl to their API

Six lines is the floor, not the ceiling. Most teams stay near the floor for years.

Next steps