To run a daily script that generates a video and uploads it to YouTube Shorts, Instagram Reels, and Facebook, you typically need an always-on server (like an AWS EC2 instance) running a cron daemon. But servers cost money, require maintenance, and occasionally go down.
For the ExamLoom Daily Automation pipeline, we bypassed servers entirely. We used GitHub Actions' scheduled workflows as a powerful, zero-maintenance, and completely free cron server.
1. Understanding the schedule Event
GitHub Actions allows you to trigger workflows on a cron schedule using POSIX cron syntax. GitHub provides generous free tier minutes (2,000 minutes/month for private repos, unlimited for public).
# .github/workflows/daily-video.yml
name: Daily ExamLoom Video Generator
on:
schedule:
# Runs at 10:00 AM UTC every day
- cron: '0 10 * * *'
# Allow manual triggering from the GitHub UI for testing
workflow_dispatch:
jobs:
generate-and-publish:
runs-on: ubuntu-latest
2. Preparing the Environment (FFmpeg)
To generate videos, our Python script relies on ffmpeg. GitHub Actions runners are clean Ubuntu environments, meaning we must install FFmpeg on the fly before running our script.
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install FFmpeg
run: |
sudo apt-get update
sudo apt-get install -y ffmpeg
- name: Install Python Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
3. Securely Handling API Keys
Publishing to social platforms requires API keys and OAuth tokens. Hardcoding these in the script is a massive security vulnerability. Instead, use GitHub Secrets.
Go to your repository settings -> Secrets and variables -> Actions, and add your keys. Then pass them into the script via environment variables.
- name: Run Video Generation & Publishing Script
env:
YOUTUBE_API_KEY: ${{ secrets.YOUTUBE_API_KEY }}
INSTAGRAM_ACCESS_TOKEN: ${{ secrets.INSTAGRAM_ACCESS_TOKEN }}
FACEBOOK_PAGE_ID: ${{ secrets.FACEBOOK_PAGE_ID }}
run: |
python generate_and_publish.py
4. Handling Idempotency and State
Cron jobs can fail. An API might timeout, or a network request might drop. If your script fails halfway through, what happens when it runs again tomorrow? Does it publish the same video twice?
Because GitHub Action runners are ephemeral, they have no persistent disk storage. To maintain state (e.g., "Which question did I publish yesterday?"), we commit a JSON file back to the repository at the end of the script.
- name: Commit state tracking file
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git add state.json
git commit -m "Update state after daily run [skip ci]" || echo "No changes to commit"
git push
Note: The [skip ci] in the commit message is crucial. It prevents the commit itself from accidentally triggering other workflows and causing an infinite loop.
Conclusion
By leveraging GitHub Actions as a cron server, we eliminated infrastructure costs, automated our daily marketing pipeline, and gained access to a free CI/CD environment with built-in secrets management and logging. It is the ultimate automation hack for solo founders and startups.
— Ankit Kumar