I kept starting my mornings the same way: opening ten tabs, skimming, closing them, and still feeling like I had missed the one thing that mattered. Paid newsletters did not really fix it, and I did not want to rent a server just to send myself an email. So over a few evenings I built a small agent that does it for me, and it costs me nothing to run.
Here is what it does: every morning it searches the web for fresh articles on the topics I care about, an LLM writes a short newspaper-style summary from the actual article text, and the result lands in my inbox. It remembers what it already sent, so the same story never shows up twice.
The part I am proud of is not the AI, it is that the whole thing runs on the GitHub Actions free tier with no server at all. This post walks through how it works and how you can deploy your own in a few minutes.
The idea: research, write, deliver
The agent is a simple loop with three jobs.
Research. It takes my list of topics and runs live web searches (I use Tavily's free tier). This is the key point: it does not hallucinate the news, it pulls real, recent articles and works from their content.
Write. It sends the article text to an LLM (I use Groq, which is fast and has a free tier) and asks for a concise, factual summary in a newspaper style. No hype, no filler, just what happened.
Deliver. It renders the briefing as an HTML email and sends it through Gmail. Before sending, it checks a small history file so it never repeats a story I have already seen.
Why GitHub Actions instead of a server
This is the design decision that makes the project actually free and actually yours.
A scheduled GitHub Actions workflow runs the agent on a timer. Your API keys live in your own repository secrets, so nothing sensitive ever leaves your account. There is no VPS to pay for, no container to babysit, no third-party service holding your data.
There is one catch worth being honest about. GitHub Actions cron scheduling is not precise, runs can drift by many minutes when the platform is busy. So instead of relying on it directly, I trigger the workflow from a free external cron pinger (cron-job.org), and the workflow itself has a time-window guard so it only ever produces one briefing per day even if it gets pinged more than once.
# .github/workflows/daily-briefing.yml (simplified)
on:
workflow_dispatch:
repository_dispatch:
types: [send-briefing]
jobs:
briefing:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install -r requirements.txt
- run: python main.py --now
env:
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
TAVILY_API_KEY: ${{ secrets.TAVILY_API_KEY }}
Not repeating yourself
An agent that emails you the same headline three days running gets muted fast. So the agent keeps a small history of what it has already sent and commits it back to the repo at the end of each run. The next morning, anything already seen is filtered out before the LLM ever touches it. It is a low-tech solution (a JSON file in git), but it is durable, transparent, and free.
Surviving free-tier reality
Building on free tiers means things change under you. A model I was using got deprecated, and one morning every run failed with a 404. That was a good lesson: do not hardcode a single model.
Now the model is configurable, with a fallback chain. If the first model is gone, the agent logs it and tries the next one, and only gives up if every option fails.
MODELS = ["openai/gpt-oss-120b", "openai/gpt-oss-20b", "qwen/qwen3.6-27b"]
def summarize(prompt):
for model in MODELS:
try:
return call_llm(model, prompt)
except ModelNotFound:
log.warning(f"{model} unavailable, trying next")
raise RuntimeError("all models failed")
Deploy your own
If you want your own morning briefing, it takes about five minutes:
- Fork or clone the repo.
- Get free API keys from Groq and Tavily.
- Add your keys as repository secrets, plus Gmail credentials for delivery.
- Edit
config.yamlwith your title, topics, and how many articles you want. - Run
python main.py --dry-runlocally to preview the briefing without sending anything. That last step matters: you can see exactly what you will get with only two API keys, no email setup required, before you commit to anything.
Honest limitations
It leans on free API tiers, so quotas and model availability can shift (the fallback chain covers most of that). Delivery timing depends on the external cron ping. And right now it is email-only, though adding another channel like Telegram would not be hard.
Try it
The code is open source (MIT): https://github.com/tballochi/daily-briefing
I would love feedback, especially from anyone who has built something similar on free infrastructure. What would you do differently?
Top comments (0)