🚀 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
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.
- 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. - 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.
- 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).
- Navigate to GitHub Developer Settings:Go to your GitHub profile settings > Developer settings > Personal access tokens > Tokens (classic).
- 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_reposcope, or simplyrepoif you prefer (which includespublic_repo). The key is to have read access to repository issues.
- 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.
- 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)
index.html(Template):This is a basic HTML template. Ourbuild.shscript 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.)build.sh(Bash Script):This script fetches open issues, determines the overall status, and then injects this information intoindex.html. ReplaceYOUR_GITHUB_USERandYOUR_REPO_NAMEwith 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.htmltopublic/index.html. gh apiis 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_STATUSbased 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 -eto perform an in-place replacement, injecting the generated status HTML into the placeholder inpublic/index.html.
- It sets up the target output directory (
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 thepublicdirectory 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.
- 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 - 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). - Configure Build Settings:Netlify should automatically detect your
netlify.toml. Confirm the “Build command” ischmod +x ./build.sh && ./build.shand the “Publish directory” ispublic. - 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 theghCLI uses by default). - Value: Paste your GitHub Personal Access Token generated in Step 2.
Click “Save”.
- Key:
- 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 generatedpublic/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.
- 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
mainormaster). Copy the generated URL. - 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”.
- Test the Automation:Go to your GitHub repository and open a new issue, or update an existing one (e.g., change a label from
investigatingtoresolved). 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 leastpublic_repoorreposcope 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.shscript (e.g., incorrect GitHub repository name, syntax errors in Bash orjqcommands) 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 yourbuild.shscript. 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.shscript to fetch closed issues with theresolvedlabel 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-itif 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.
🤖 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