Skip to Content
GuideIntroductionYour First Workflow

Your First Workflow

Now that you understand the core concepts, let’s build a practical workflow from scratch. This guide will walk you through creating a comprehensive monitoring workflow for a web application.

The Scenario

We’ll create a workflow that monitors a complete web application stack:

  1. Frontend: Check if the web application loads correctly
  2. API: Verify that the REST API is responding
  3. Database: Test database connectivity through the API
  4. External Services: Check third-party service integrations

Step 1: Basic Structure

Let’s start with the basic workflow structure:

name: Web Application Health Check description: Comprehensive monitoring for our web application stack jobs: # We'll add jobs here

Step 2: Frontend Monitoring

Add a job to check the frontend application:

name: Web Application Health Check description: Comprehensive monitoring for our web application stack jobs: - id: frontend-check name: Frontend Application Check steps: - name: Check Homepage id: frontend-check uses: http with: url: https://myapp.example.com method: GET headers: User-Agent: "Probe Health Check" test: res.code == 200 && (rt.sec * 1000) < 3000 outputs: homepage_response_time: (rt.sec * 1000) - name: Check Critical Page uses: http with: url: https://myapp.example.com/dashboard method: GET test: res.code == 200 || res.code == 302 - name: Report Frontend Status uses: hello echo: "✅ Frontend is healthy ({{outputs.homepage_response_time}}ms)"

Step 3: API Monitoring

Add a separate job for API monitoring:

api-check: name: API Health Check steps: - name: Check API Health Endpoint id: health-check uses: http with: url: https://api.myapp.example.com/health method: GET headers: Accept: "application/json" test: res.code == 200 && res.body.status == "healthy" outputs: api_version: res.body.version database_status: res.body.database - name: Test User Authentication uses: http with: url: https://api.myapp.example.com/auth/login method: POST headers: Content-Type: "application/json" body: | { "username": "healthcheck", "password": "{{vars.HEALTH_CHECK_PASSWORD}}" } test: res.code == 200 && res.body.token != null outputs: auth_token: res.body.token - name: Test Authenticated Endpoint uses: http with: url: https://api.myapp.example.com/user/profile method: GET headers: Authorization: "Bearer {{outputs.auth_token}}" test: res.code == 200 - name: Report API Status uses: hello echo: "✅ API v{{outputs.api_version}} is healthy"

Step 4: Adding Dependencies

Make the API check depend on successful frontend check:

api-check: name: API Health Check needs: [frontend-check] # Wait for frontend to be healthy steps: # ... existing steps

Step 5: External Service Checks

Add checks for external services:

external-services: name: External Services Check steps: - name: Check Email Service uses: http with: url: https://api.sendgrid.com/v3/mail/send method: POST headers: Authorization: "Bearer {{vars.SENDGRID_API_KEY}}" Content-Type: "application/json" body: | { "from": {"email": "health@myapp.example.com"}, "subject": "Health Check Test", "content": [{"type": "text/plain", "value": "Test"}], "personalizations": [{"to": [{"email": "test@myapp.example.com"}]}] } test: res.code == 202 - name: Check Payment Gateway uses: http with: url: https://api.stripe.com/v1/charges method: GET headers: Authorization: "Bearer {{vars.STRIPE_SECRET_KEY}}" test: res.code == 200 - name: Report External Services uses: hello echo: "✅ All external services are responding"

Step 6: Error Handling and Notifications

Add error handling and notification logic:

notification: name: Send Notifications needs: [frontend-check, api-check, external-services] steps: - name: Success Notification uses: hello echo: | 🎉 All systems are healthy! Frontend: ✅ ({{outputs['frontend-check'].homepage_response_time}}ms) API: ✅ v{{outputs['api-check'].api_version}} External Services: ✅ Monitoring completed at {{unixtime()}} - name: Failure Notification uses: hello echo: | 🚨 ALERT: System health check failed! Please investigate immediately.

Complete Workflow

Here’s the complete workflow file (health-check.yml):

name: Web Application Health Check description: Comprehensive monitoring for our web application stack jobs: - id: frontend-check name: Frontend Application Check steps: - name: Check Homepage id: frontend-check uses: http with: url: https://myapp.example.com method: GET headers: User-Agent: "Probe Health Check" test: res.code == 200 && (rt.sec * 1000) < 3000 outputs: homepage_response_time: (rt.sec * 1000) - name: Check Critical Page uses: http with: url: https://myapp.example.com/dashboard method: GET test: res.code == 200 || res.code == 302 - name: Report Frontend Status uses: hello echo: "✅ Frontend is healthy ({{outputs.homepage_response_time}}ms)" - id: api-check name: API Health Check needs: [frontend-check] steps: - name: Check API Health Endpoint id: health-check uses: http with: url: https://api.myapp.example.com/health method: GET headers: Accept: "application/json" test: res.code == 200 && res.body.status == "healthy" outputs: api_version: res.body.version database_status: res.body.database - name: Test User Authentication id: api-check uses: http with: url: https://api.myapp.example.com/auth/login method: POST headers: Content-Type: "application/json" body: | { "username": "healthcheck", "password": "{{vars.HEALTH_CHECK_PASSWORD}}" } test: res.code == 200 && res.body.token != null outputs: auth_token: res.body.token - name: Test Authenticated Endpoint uses: http with: url: https://api.myapp.example.com/user/profile method: GET headers: Authorization: "Bearer {{outputs.auth_token}}" test: res.code == 200 - name: Report API Status uses: hello echo: "✅ API v{{outputs.api_version}} is healthy" - id: external-services name: External Services Check steps: - name: Check Email Service uses: http with: url: https://api.sendgrid.com/v3/mail/send method: POST headers: Authorization: "Bearer {{vars.SENDGRID_API_KEY}}" Content-Type: "application/json" body: | { "from": {"email": "health@myapp.example.com"}, "subject": "Health Check Test", "content": [{"type": "text/plain", "value": "Test"}], "personalizations": [{"to": [{"email": "test@myapp.example.com"}]}] } test: res.code == 202 - name: Check Payment Gateway uses: http with: url: https://api.stripe.com/v1/charges method: GET headers: Authorization: "Bearer {{vars.STRIPE_SECRET_KEY}}" test: res.code == 200 - name: Report External Services uses: hello echo: "✅ All external services are responding" - id: notification name: Send Notifications needs: [frontend-check, api-check, external-services] steps: - name: Success Notification uses: hello echo: | 🎉 All systems are healthy! Frontend: ✅ ({{outputs['frontend-check'].homepage_response_time}}ms) API: ✅ v{{outputs['api-check'].api_version}} External Services: ✅ Monitoring completed at {{unixtime()}} - name: Failure Notification uses: hello echo: | 🚨 ALERT: System health check failed! Please investigate immediately.

Running the Workflow

The workflow reads its values from the environment, so those are set first, and the run can then be repeated with more detail when something needs checking.

Set Environment Variables

First, set up your environment variables:

export HEALTH_CHECK_PASSWORD="your-test-password" export SENDGRID_API_KEY="your-sendgrid-key" export STRIPE_SECRET_KEY="your-stripe-key"

Execute the Workflow

Run the workflow:

probe health-check.yml

Use Verbose Mode for Debugging

For detailed output during development:

probe -v health-check.yml

Making It Production-Ready

Three things separate this workflow from one that can be left running: configuration per environment, tolerance for transient failures, and a schedule.

1. Environment-Specific Configuration

Create environment-specific config files:

production.yml:

# Override URLs for production variables: frontend_url: https://app.mycompany.com api_url: https://api.mycompany.com

staging.yml:

# Override URLs for staging variables: frontend_url: https://staging.mycompany.com api_url: https://api-staging.mycompany.com

Run with environment-specific config:

probe health-check.yml,production.yml

2. Add Retry Logic

A check that runs unattended should tolerate a single transient failure.

- name: Check Critical Service uses: http with: url: https://critical-service.example.com method: GET retry_count: 3 retry_delay: 5s test: res.code == 200

3. Set up Monitoring Schedule

Use cron to run regularly:

# Add to crontab - run every 5 minutes */5 * * * * /usr/local/bin/probe /path/to/health-check.yml

What You’ve Learned

In this guide, you’ve learned how to:

  • ✅ Structure a multi-job workflow
  • ✅ Use job dependencies with needs
  • ✅ Pass data between steps using outputs
  • ✅ Handle authentication in API calls
  • Implement conditional logic with skipif
  • ✅ Use environment variables for configuration
  • ✅ Create comprehensive error handling
  • ✅ Merge configuration files for different environments

Next Steps

Ready to dive deeper? Here are your next steps:

  1. Master the CLI - Learn all command-line options
  2. Explore How-tos - See specific use cases and patterns
  3. Browse the Reference - Deep dive into all available features

The workflow you’ve built is a solid foundation. You can extend it by adding more checks, integrating with monitoring systems, or customizing it for your specific application stack.

Updated at