🚀 Executive Summary

TL;DR: This guide provides a cost-effective solution for creating a dynamic status page by leveraging GitHub Issues for incident management and Netlify for static site hosting. It streamlines communication by using incident data directly from GitHub Issues to automatically update the status page, minimizing manual overhead and costs.

🎯 Key Takeaways

  • GitHub Issues serve as the content source for the status page, with labels (e.g., ‘investigating’, ‘major outage’) defining incident status and severity.
  • A `build.sh` Bash script, executed during Netlify deployments, fetches open GitHub issues using `gh api` and `jq`, then dynamically injects formatted incident data into a static `index.html` template.
  • Automation is achieved by configuring Netlify build hooks and GitHub webhooks, ensuring the status page automatically updates whenever an issue is created or modified in the designated GitHub repository.

Creating a Status Page using GitHub Issues and Netlify

Creating a Status Page using GitHub Issues and Netlify

Introduction

In today’s fast-paced digital landscape, maintaining transparency and keeping users informed about system status is paramount. Whether you’re running a small web service or a complex microservices architecture, unexpected outages or performance degradations are inevitable. The challenge often lies in communicating these incidents effectively without adding significant overhead or incurring hefty costs for dedicated status page services.

Manual updates are tedious and error-prone, consuming valuable time during critical incidents. Meanwhile, many enterprise-grade status page SaaS solutions, while feature-rich, can be prohibitively expensive for startups, side projects, or even established teams looking for a cost-effective alternative.

What if we told you there’s a robust, highly customizable, and virtually free way to build a status page? This tutorial will guide you through creating a dynamic status page by leveraging two powerful and widely-used tools: GitHub Issues for incident management and Netlify for seamless static site hosting. This approach allows your incident management to double as your status page content source, streamlining your communication workflow and keeping costs to a minimum.

Prerequisites

Before we dive into the implementation, ensure you have the following set up:

  • A GitHub Account: This is where your issues will live.
  • A Netlify Account: For hosting your static status page.
  • Basic familiarity with HTML and CSS: To customize the look and feel of your status page.
  • Basic understanding of the command line interface (CLI).
  • The GitHub CLI (gh) installed locally: While not strictly required for Netlify builds, it’s invaluable for testing your API calls and managing issues locally.

Step-by-Step Guide

Step 1: Set Up Your GitHub Repository and Issues

First, we need a dedicated GitHub repository to house your status page issues. These issues will represent incidents or operational updates.

  1. Create a New GitHub Repository:Go to GitHub and create a new public repository (e.g., my-service-status). Make sure it’s public so our static site can fetch issues without complex authentication for public information.
  2. Define Labels for Status:Labels are crucial for categorizing the status of your services. Navigate to your new repository’s “Issues” tab, then “Labels”. Create the following labels (or similar, adjust as needed):
    • operational (green color): For general system health (often not directly applied to incidents but can be a default state).
    • investigating (orange color): When an incident is detected and being looked into.
    • degraded performance (yellow color): Services are experiencing issues but are still partially functional.
    • major outage (red color): Services are completely unavailable.
    • resolved (blue color): The incident has been mitigated and services are restored.
  3. Create a Sample Issue:Create an issue to test. Give it a descriptive title (e.g., “API Latency Spike in EU Region”). Apply a label like investigating. Add comments for updates as the incident progresses.

Step 2: Generate a GitHub Personal Access Token (PAT)

To programmatically fetch issues from your repository during the Netlify build process, we need a GitHub Personal Access Token (PAT).

  1. Navigate to GitHub Developer Settings:Go to your GitHub profile settings > Developer settings > Personal access tokens > Tokens (classic).
  2. Generate a New Token:Click “Generate new token (classic)”.
    • Note: While finer-grained tokens are available, for simplicity and compatibility with older APIs, classic tokens are often used for build scripts.
    • Give your token a descriptive name (e.g., “Netlify Status Page Builder”).
    • Set an appropriate expiration (e.g., 90 days or custom).
    • For public repositories, you typically only need public_repo scope, or simply repo if you prefer (which includes public_repo). The key is to have read access to repository issues.
  3. Copy the Token:IMPORTANT: Copy the generated token immediately. You won’t be able to see it again.

Step 3: Create Your Static Status Page and Build Script

Now, let’s create the core of our status page. We’ll use a simple HTML file and a Bash script that runs during the Netlify build to fetch and display the latest status from your GitHub issues.

  1. Project Structure:Create a new directory for your project (e.g., status-page). Inside it, create the following files:
    • index.html (your main status page template)
    • build.sh (the script to fetch issues and generate content)
    • netlify.toml (Netlify configuration)
  2. index.html (Template):This is a basic HTML template. Our build.sh script will inject content into the <div id="status-container"></div> element.
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>My Service Status</title>
        <style>
            body { font-family: Arial, sans-serif; margin: 20px; background-color: #f4f7f6; color: #333; }
            .container { max-width: 800px; margin: auto; background: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
            h1, h2 { color: #0056b3; }
            .status-header { display: flex; align-items: center; margin-bottom: 20px; }
            .status-indicator { width: 20px; height: 20px; border-radius: 50%; margin-right: 10px; }
            .operational { background-color: #28a745; } /* Green */
            .degraded-performance { background-color: #ffc107; } /* Yellow */
            .major-outage { background-color: #dc3545; } /* Red */
            .investigating { background-color: #fd7e14; } /* Orange */
            .resolved { background-color: #007bff; } /* Blue */
            .issue { background-color: #f8f9fa; border: 1px solid #e9ecef; padding: 15px; margin-bottom: 15px; border-radius: 5px; }
            .issue-title { font-weight: bold; margin-bottom: 5px; }
            .issue-date { font-size: 0.8em; color: #6c757d; margin-bottom: 10px; }
            .issue-body { font-size: 0.9em; line-height: 1.5; }
            .no-incidents { color: #28a745; font-weight: bold; }
        </style>
    </head>
    <body>
        <div class="container">
            <h1>Service Status for TechResolve</h1>
            <div id="status-container">
                <!-- Content will be injected here by build.sh -->
            </div>
        </div>
    </body>
    </html>
    

    (Note: The <style> block is embedded for tutorial simplicity. In a real project, you’d link an external CSS file.)

  3. build.sh (Bash Script):This script fetches open issues, determines the overall status, and then injects this information into index.html. Replace YOUR_GITHUB_USER and YOUR_REPO_NAME with your actual GitHub username and repository name.
    
    #!/bin/bash
    
    # Exit immediately if a command exits with a non-zero status.
    set -e
    
    # --- Configuration ---
    GITHUB_REPO="YOUR_GITHUB_USER/YOUR_REPO_NAME" # e.g., techresolve/my-service-status
    OUTPUT_DIR="public"
    STATUS_TEMPLATE="index.html"
    # --- End Configuration ---
    
    # Ensure output directory exists
    mkdir -p "$OUTPUT_DIR"
    
    # Copy the template HTML to the output directory
    cp "$STATUS_TEMPLATE" "$OUTPUT_DIR/index.html"
    
    # Fetch open issues from GitHub
    # We're specifically looking for issues with labels that signify an ongoing incident.
    # The `gh` CLI uses the GITHUB_TOKEN environment variable for authentication.
    ISSUES_JSON=$(gh api \
      -H "Accept: application/vnd.github.v3+json" \
      "/repos/$GITHUB_REPO/issues?state=open&labels=investigating,degraded%20performance,major%20outage" \
      --jq '[.[] | {number: .number, title: .title, body: .body, created_at: .created_at, labels: [.labels[].name]}]')
    
    # Determine overall status
    OVERALL_STATUS="operational"
    STATUS_TEXT="All Systems Operational"
    STATUS_CLASS="operational"
    
    if [ "$(echo "$ISSUES_JSON" | jq 'length')" -gt 0 ]; then
      # Check for highest severity issue
      if echo "$ISSUES_JSON" | jq '.[].labels[]' | grep -q "major outage"; then
        OVERALL_STATUS="major outage"
        STATUS_TEXT="Major Outage"
        STATUS_CLASS="major-outage"
      elif echo "$ISSUES_JSON" | jq '.[].labels[]' | grep -q "degraded performance"; then
        OVERALL_STATUS="degraded performance"
        STATUS_TEXT="Degraded Performance"
        STATUS_CLASS="degraded-performance"
      elif echo "$ISSUES_JSON" | jq '.[].labels[]' | grep -q "investigating"; then
        OVERALL_STATUS="investigating"
        STATUS_TEXT="Incident Investigating"
        STATUS_CLASS="investigating"
      fi
    fi
    
    # Build the status HTML content
    STATUS_CONTENT="
    <div class='status-header'>
        <div class='status-indicator $STATUS_CLASS'></div>
        <h2>$STATUS_TEXT</h2>
    </div>
    "
    
    if [ "$(echo "$ISSUES_JSON" | jq 'length')" -eq 0 ]; then
      STATUS_CONTENT+="<p class='no-incidents'>No active incidents reported.</p>"
    else
      # Loop through issues and format them
      echo "$ISSUES_JSON" | jq -c '.[]' | while read -r ISSUE; do
        ISSUE_TITLE=$(echo "$ISSUE" | jq -r '.title')
        ISSUE_BODY=$(echo "$ISSUE" | jq -r '.body')
        ISSUE_DATE=$(echo "$ISSUE" | jq -r '.created_at | split("T")[0]') # Just date part
        
        # Simple HTML for the issue
        STATUS_CONTENT+="
        <div class='issue'>
            <div class='issue-title'>Issue #$(echo "$ISSUE" | jq -r '.number'): $ISSUE_TITLE</div>
            <div class='issue-date'>Reported: $ISSUE_DATE</div>
            <div class='issue-body'>$(echo "$ISSUE_BODY" | sed 's/\\n/<br>/g')</div>
        </div>
        "
      done
    fi
    
    # Inject the generated status content into the template
    # We use Perl for in-place editing to handle potential special characters in content
    perl -pi -e "s|<!-- Content will be injected here by build.sh -->|$STATUS_CONTENT|" "$OUTPUT_DIR/index.html"
    
    echo "Status page generated successfully in $OUTPUT_DIR/index.html"
    

    Logic Explanation for build.sh:

    • It sets up the target output directory (public).
    • It copies the base index.html to public/index.html.
    • gh api is used to fetch open issues from your specified GitHub repository. We filter by specific labels (investigating, degraded performance, major outage) to only show active incidents.
    • The `jq` command is a powerful JSON processor used to parse the API response and extract relevant fields like issue number, title, body, creation date, and labels.
    • The script then determines the OVERALL_STATUS based on the severity of the open issues (e.g., if there’s a “major outage” issue, that becomes the primary status).
    • It constructs HTML snippets for the overall status indicator and for each individual active incident.
    • Finally, it uses perl -pi -e to perform an in-place replacement, injecting the generated status HTML into the placeholder in public/index.html.
  4. netlify.toml (Netlify Configuration):This file tells Netlify how to build and deploy your site.
    
    [build]
      publish = "public"
      command = "chmod +x ./build.sh && ./build.sh"
    
    [build.environment]
      # IMPORTANT: GITHUB_TOKEN will be set in Netlify UI for security
      # This section is just for local testing if you want to run `build.sh`
      # GITHUB_TOKEN = "YOUR_PERSONAL_ACCESS_TOKEN_HERE" 
      # Remember to remove or comment this out before pushing to Git if it contains sensitive data.
    

    Logic Explanation for netlify.toml:

    • publish = "public": Tells Netlify that the static files to serve will be in the public directory after the build.
    • command = "chmod +x ./build.sh && ./build.sh": This is the build command Netlify will execute. It first makes our script executable, then runs it.
    • [build.environment]: This section is where you could define environment variables for local testing. However, for the GitHub PAT, we’ll configure it securely in Netlify’s UI.

Step 4: Deploy to Netlify

With your files ready, let’s deploy your status page.

  1. Commit and Push to GitHub:Initialize a Git repository in your project folder, commit all your files (index.html, build.sh, netlify.toml), and push them to your GitHub repository (the same one you created in Step 1).
    
    git init
    git add .
    git commit -m "Initial status page setup"
    git branch -M main
    git remote add origin https://github.com/YOUR_GITHUB_USER/YOUR_REPO_NAME.git
    git push -u origin main
    
  2. Connect to Netlify:Log in to Netlify. Click “Add new site” > “Import an existing project”. Connect your GitHub account and select the repository you just pushed (e.g., my-service-status).
  3. Configure Build Settings:Netlify should automatically detect your netlify.toml. Confirm the “Build command” is chmod +x ./build.sh && ./build.sh and the “Publish directory” is public.
  4. Set Environment Variable:This is crucial for security. Go to “Site settings” > “Build & deploy” > “Environment”. Click “Add a variable”.
    • Key: GITHUB_TOKEN (This is what the gh CLI uses by default).
    • Value: Paste your GitHub Personal Access Token generated in Step 2.

    Click “Save”.

  5. Deploy Your Site:Trigger a deploy from the Netlify UI (or it might start automatically after saving the environment variable). Netlify will clone your repo, run build.sh, and then serve the generated public/index.html.

Step 5: Automate Updates with Netlify Build Hooks

To ensure your status page is always up-to-date without manual intervention, we’ll set up a Netlify build hook that GitHub can trigger.

  1. Create a Build Hook in Netlify:In your Netlify site settings, go to “Build & deploy” > “Build hooks”. Click “Add build hook”. Give it a name (e.g., “GitHub Issues Update”) and select the branch you want to deploy from (usually main or master). Copy the generated URL.
  2. Configure a GitHub Webhook:Go to your GitHub status repository > “Settings” > “Webhooks”. Click “Add webhook”.
    • Payload URL: Paste the Netlify build hook URL.
    • Content type: Select application/json.
    • Which events would you like to trigger this webhook? Select “Let me select individual events”. Check Issues.
    • Active: Ensure this is checked.

    Click “Add webhook”.

  3. Test the Automation:Go to your GitHub repository and open a new issue, or update an existing one (e.g., change a label from investigating to resolved). After a few moments, you should see a new build triggered in Netlify, and your status page will update automatically!

Common Pitfalls

  • Incorrect GitHub PAT Permissions: If your Netlify build fails with a GitHub API error (e.g., 401 Unauthorized or 403 Forbidden), double-check that your Personal Access Token (GITHUB_TOKEN) has the necessary read permissions (at least public_repo or repo scope for classic tokens) and is correctly set in Netlify’s environment variables.
  • GitHub API Rate Limits: For very high-traffic sites or complex issue fetching, you might hit GitHub’s API rate limits (5,000 requests per hour for authenticated users). For a typical status page, this is rarely an issue, but be mindful if you expand the script significantly. Unauthenticated requests have a much lower limit (60 requests per hour), which is why a PAT is crucial.
  • Build Script Errors: If your status page is blank or doesn’t update, check the Netlify deploy logs. Errors in the build.sh script (e.g., incorrect GitHub repository name, syntax errors in Bash or jq commands) will halt the build process.
  • Issue Label Mismatch: Ensure the labels you use in your GitHub issues (e.g., major outage) exactly match those being filtered and checked in your build.sh script. Typos are common.

Conclusion

You’ve now successfully built a functional and automated status page leveraging the power of GitHub Issues and Netlify! This solution provides a cost-effective, transparent, and efficient way to communicate system status to your users. By integrating incident management directly with your communication platform, you minimize manual overhead during stressful situations and maintain a single source of truth.

From here, you can further enhance your status page:

  • Custom Domain: Connect a custom domain to your Netlify site for a more professional look.
  • Advanced Styling: Refine the CSS to match your brand’s aesthetics.
  • Historical Incidents: Modify the build.sh script to fetch closed issues with the resolved label and display them in a “past incidents” section.
  • Service-Specific Status: Extend the system to show status for multiple services (e.g., using different issue labels or even separate repositories for each service).
  • Markdown Rendering: Use a Markdown parser (like markdown-it if you convert to a Node.js build script) to render the issue body more nicely.

Embrace the power of simple, yet effective, DevOps tooling to keep your users informed and your operations smooth.

 

Darian Vance - Lead Cloud Architect

Darian Vance

Lead Cloud Architect & DevOps Strategist

With over 12 years in system architecture and automation, Darian specializes in simplifying complex cloud infrastructures. An advocate for open-source solutions, he founded TechResolve to provide engineers with actionable, battle-tested troubleshooting guides and robust software alternatives.


🤖 Frequently Asked Questions

âť“ How can I create a free status page using existing tools?

You can create a free status page by using GitHub Issues to track incidents and Netlify to host a static site. A build script fetches issue data from GitHub and dynamically generates the status page content during Netlify deployments.

âť“ How does this GitHub Issues/Netlify approach compare to dedicated status page SaaS?

This approach is virtually free, highly customizable, and integrates incident management directly with the communication platform, reducing overhead. Dedicated status page SaaS solutions, while feature-rich, can be prohibitively expensive for startups or projects seeking cost-effective alternatives.

âť“ What is a common implementation pitfall when setting up the GitHub Personal Access Token (PAT) for this status page?

A common pitfall is incorrect GitHub PAT permissions. Ensure the `GITHUB_TOKEN` has at least `public_repo` or `repo` scope for classic tokens and is correctly set as an environment variable in Netlify to avoid 401/403 API errors during the build process.

Leave a Reply

Discover more from TechResolve - SaaS Troubleshooting & Software Alternatives

Subscribe now to keep reading and get access to the full archive.

Continue reading