```html

You have a Python script that needs to run every six hours. Or every Monday at 9 AM. Or whenever you push new data to a spreadsheet. You could rent a server, configure cron, manage SSH keys, and pay monthly for uptime you don't fully use. Or you could write 15 lines of YAML, push to GitHub, and let their infrastructure handle it for free.

Key Takeaways

  • GitHub Actions runs scheduled Python scripts for free on public repositories, with 2,000 minutes per month on private repositories
  • You control timing using cron syntax in a YAML file — intervals as frequent as every 5 minutes
  • Workflows require explicit dependency installation — GitHub does not include pandas, requests, or other third-party libraries by default
Difficulty: Beginner Time needed: 15–20 minutes For: Python developers who want automated script execution without managing servers

Before You Start

This guide assumes you have a Python script you want to run repeatedly and a GitHub account with push access to a repository. You should understand basic Git operations — commit, push — and have tested your script locally. The script must complete within 6 hours. GitHub terminates longer-running workflows automatically.

What You Need

  • A GitHub account (free tier works for public repositories)
  • A GitHub repository where you have write access
  • A Python script that runs successfully on your local machine
  • List of any third-party dependencies your script requires (pandas, requests, beautifulsoup4)
  • Basic familiarity with YAML syntax

Step 1: Create the Workflows Directory

GitHub Actions looks for workflow files in one place only. In your repository's root directory, create a folder named .github, then create a workflows subfolder inside it. The complete path: .github/workflows/.

This nested structure is required. GitHub will not detect workflows stored anywhere else. If you're working locally, create these directories using mkdir -p .github/workflows before committing.

Step 2: Write the Workflow YAML File

Inside .github/workflows/, create a new file with a .yml or .yaml extension. Name it descriptively: schedule-script.yml. This file defines when and how your script runs.

Start with this template:

name: Scheduled Python Script
on:
  schedule:
    - cron: '0 */6 * * *'
jobs:
  run-script:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
      - name: Run script
        run: python your_script.py

Replace your_script.py with your actual script filename. The cron expression '0 */6 * * *' means "every 6 hours at minute 0" — adjust this based on your needs using standard cron syntax. What happens next is what most tutorials skip.

a close up of a computer screen with a bunch of words on it
Photo by Gabriel Heinzer / Unsplash

Step 3: Configure the Cron Schedule

The cron field accepts standard Unix cron syntax with five time fields: minute, hour, day of month, month, and day of week. GitHub Actions interprets these in UTC time, not your local timezone.

Common patterns: '0 0 * * *' runs daily at midnight UTC. '*/15 * * * *' runs every 15 minutes. '0 9 * * 1' runs Mondays at 9 AM UTC.

GitHub enforces a minimum interval of 5 minutes but may delay scheduled runs during high platform load. Do not rely on exact-second precision. If you need a script to run at 9 AM Eastern Time, calculate the UTC equivalent — typically 14:00 UTC in winter, 13:00 UTC in summer due to daylight saving. This timezone conversion catches more people than the YAML syntax does.

Step 4: Specify Python Dependencies

If your script imports libraries beyond the standard library, create a requirements.txt file in your repository root. List each package on a separate line: pandas==2.1.4, requests==2.31.0.

The workflow's "Install dependencies" step runs pip install -r requirements.txt before executing your script. Pin specific versions to prevent unexpected breakage when package maintainers release updates. If your script has no external dependencies, remove the requirements installation step from the YAML file entirely.

Step 5: Commit and Push the Workflow

Add the workflow file to Git with git add .github/workflows/schedule-script.yml, commit it with git commit -m "Add scheduled workflow", and push to GitHub with git push origin main. Replace main with your default branch name if different.

GitHub Actions activates immediately after the push completes. You do not need to configure anything in the GitHub web interface. The presence of the YAML file in the correct directory is sufficient.

Step 6: Verify the Workflow Runs

Navigate to your repository on GitHub, click the Actions tab at the top, and look for your workflow name in the left sidebar. The first scheduled run will not occur until the next cron interval — you cannot see results immediately.

To test without waiting, add a workflow_dispatch: trigger under the on: section in your YAML file, push the change, then click Run workflow in the Actions tab. Successful runs show a green checkmark. Failed runs display a red X with logs explaining what broke.

Common Problems

Workflow does not appear in Actions tab: Confirm the .github/workflows/ directory structure is exact. Extra spaces, wrong nesting, or files outside this path will not register. YAML syntax errors also prevent registration — use a YAML validator to check formatting before committing.

Script fails with "ModuleNotFoundError": You forgot to install dependencies. Add a requirements.txt file with the missing package name, then include the pip install step in your workflow. GitHub's Python environment is minimal — it does not include pandas, NumPy, requests, or any library you did not explicitly install.

Scheduled runs do not trigger at expected times: GitHub Actions uses UTC time, not your local timezone. High platform demand can delay runs by several minutes. Do not use GitHub Actions for time-critical tasks requiring sub-minute precision.

Best Practices

  • Store API keys and sensitive credentials in GitHub repository secrets, not hardcoded in your script — access them via environment variables using ${{ secrets.YOUR_SECRET_NAME }} in the workflow file
  • Add error handling and logging to your Python script so you can diagnose failures from GitHub Actions logs without needing local reproduction
  • Use actions/cache to cache pip dependencies between runs — this reduces installation time from 30–60 seconds to under 5 seconds for scripts with many packages
  • Set a maximum execution time with timeout-minutes: 30 under the job definition to prevent runaway scripts from consuming your monthly minutes allowance
  • Pin the Python version explicitly (python-version: '3.11') rather than using '3.x' — GitHub's "latest" Python may introduce breaking changes between runs

When Not to Use This

GitHub Actions is not suitable for scripts requiring execution intervals under 5 minutes — the platform enforces this minimum and may throttle more frequent attempts. Avoid using it for workloads that consistently exceed 2,000 total minutes per month on private repositories. Public repositories have higher limits but are not unlimited.

If your script needs persistent state between runs, interacts with databases requiring static IP addresses, or processes large files exceeding several gigabytes, consider dedicated infrastructure instead. GitHub Actions workflows run in ephemeral containers — all files disappear after execution unless you explicitly save them to external storage or the repository itself.

What This Really Means

Here's what most coverage of GitHub Actions misses: this isn't just a free alternative to cron. It's a shift in how small automation works. For years, running a scheduled script meant choosing between paying for a server you mostly don't use, or cobbling together free-tier cloud functions with complex IAM permissions and deployment pipelines. GitHub Actions makes the entire decision tree disappear. You write a script. You write 15 lines of YAML. You push. It runs.

The constraint is the 6-hour maximum runtime and the monthly minutes cap on private repositories. The opportunity is that most automation tasks — data collection, report generation, API polling, backup scripts — finish in under 5 minutes. For that workload, you now have infrastructure that costs nothing, requires no SSH key management, and survives as long as your repository does.

FAQ

Can I run multiple Python scripts on different schedules in the same repository?

Yes. Create separate YAML files in .github/workflows/ with distinct names and cron schedules. Each file defines an independent workflow. You can also define multiple jobs within a single workflow file, though they will share the same trigger schedule.

How do I save script output so I can review it later?

Write output to a file in your script, then use the actions/upload-artifact@v4 action to save it. Add this step after running your script: - uses: actions/upload-artifact@v4
  with:
    name: script-output
    path: output.txt
. Artifacts persist for 90 days by default and appear in the workflow run summary page.

What happens if my script fails during a scheduled run?

GitHub marks the workflow run as failed and sends an email notification to the repository owner by default. The script does not retry automatically — you must either fix the issue and wait for the next scheduled run, or manually trigger a new run from the Actions tab. Consider adding retry logic inside your Python script for transient errors like network timeouts.

Can I use this for scripts that need to interact with private data or paid APIs?

Yes, but store credentials in GitHub encrypted secrets rather than committing them to your repository. Access secrets in your workflow with ${{ secrets.API_KEY }} and pass them as environment variables to your script. Be cautious with public repositories — workflow logs are public by default and may expose sensitive data if your script prints it to stdout.

```