tutorial

How to Monitor Pkl-Configured Services with Vigilmon

Pkl (pronounced "pickle") is Apple's open-source configuration language and toolchain — a typed, programmable alternative to YAML and JSON for application co...

Pkl (pronounced "pickle") is Apple's open-source configuration language and toolchain — a typed, programmable alternative to YAML and JSON for application configuration, Kubernetes manifests, and infrastructure definitions. When you use Pkl to manage the configuration of your production services, mistakes in that configuration are what cause outages. Vigilmon gives you the external signal that tells you when a configuration change — or anything else — has taken a service down.

In this tutorial you'll set up external uptime monitoring for services managed with Pkl configuration using Vigilmon — free tier, no credit card required.


Why Pkl-managed services need external monitoring

Pkl validates your configuration types and constraints at generation time. It does not validate that the resulting configuration produces a healthy, reachable service at runtime:

  • Type-valid configuration can still be wrong — a Pkl schema can enforce that port is an integer between 1 and 65535, but it can't verify that the service actually starts on that port or that the port is reachable through your network stack
  • Configuration drift between environments — Pkl makes it easy to template config across environments, but a subtle environment variable or override mismatch can cause prod to diverge from staging without any Pkl validation failure
  • Generated Kubernetes manifests — when Pkl generates Deployment and Service manifests, a resource limit that's valid in Pkl terms can OOM-kill pods at runtime; a misconfigured readiness probe can keep pods in NotReady indefinitely
  • Deployment pipeline success ≠ working service — a CI pipeline that generates Pkl config, renders it to YAML, and applies it can exit 0 while the resulting service is crash-looping
  • Configuration hot-reload failures — services that reload Pkl-generated config at runtime (via file watch or signal) can fail to apply a new config silently, serving stale configuration that diverges from your source of truth

Vigilmon adds the external observability layer that Pkl's tooling deliberately doesn't cover: continuous probing of your services from multiple geographic regions, with instant alerts when something stops working.


What you'll need

  • Pkl installed (brew install pkl on macOS, or download from pkl-lang.org)
  • A service whose configuration is managed with Pkl
  • A network-accessible endpoint for that service
  • A free Vigilmon account (sign up takes 30 seconds)

Step 1: Define your service configuration with Pkl and include health endpoints

Start with a typed Pkl configuration that includes your service's health check URL — making health monitoring an explicit, typed part of your service definition.

// ServiceConfig.pkl
module ServiceConfig

/// The name of this service
name: String

/// HTTP port the service listens on
port: UInt16(isBetween(1024, 65535))

/// Base URL for external access
baseUrl: String(startsWith("https://"))

/// Path for the health check endpoint
healthPath: String(startsWith("/")) = "/health"

/// Full health check URL (computed)
healthUrl: String = "\(baseUrl)\(healthPath)"

/// Vigilmon monitor name (computed)
vigilmonMonitorName: String = "pkl/\(name)"

Then instantiate it for each environment:

// config/production.pkl
import "ServiceConfig.pkl"

name = "user-api"
port = 8080
baseUrl = "https://api.yourcompany.com"
healthPath = "/healthz"

Render the configuration to JSON for use by your application:

pkl eval --format json config/production.pkl
# {
#   "name": "user-api",
#   "port": 8080,
#   "baseUrl": "https://api.yourcompany.com",
#   "healthPath": "/healthz",
#   "healthUrl": "https://api.yourcompany.com/healthz",
#   "vigilmonMonitorName": "pkl/user-api"
# }

Add a health endpoint to your service that matches the healthPath in your Pkl config:

// Express.js/TypeScript example
import config from './config.json'; // generated from Pkl

app.get(config.healthPath, (req, res) => {
  res.json({
    status: 'ok',
    service: config.name,
    version: process.env.npm_package_version,
    ts: new Date().toISOString()
  });
});

Verify it responds:

curl "$(pkl eval --format json config/production.pkl | jq -r '.healthUrl')"
# {"status":"ok","service":"user-api","version":"2.1.0","ts":"2024-01-15T10:23:00Z"}

Step 2: Set up HTTP monitoring in Vigilmon

  1. Log in to vigilmon.online and go to Monitors → New Monitor
  2. Choose HTTP / HTTPS
  3. Set the URL to your service's health endpoint (from your Pkl config's healthUrl field)
  4. Set the check interval to 1 minute
  5. Under Expected response:
    • Status code: 200
    • (Optional) Response body contains: "status":"ok"
  6. Save the monitor

Generating Vigilmon monitors from Pkl config

Pkl's programmability makes it practical to generate Vigilmon monitor configuration directly from your service definitions. Create a Pkl module for Vigilmon:

// VigilmonMonitor.pkl
module VigilmonMonitor

import "ServiceConfig.pkl"

/// Generate a Vigilmon API request body from a ServiceConfig
function fromServiceConfig(svc: ServiceConfig): Mapping<String, Any> = new Mapping {
  ["name"] = svc.vigilmonMonitorName
  ["type"] = "http"
  ["url"] = svc.healthUrl
  ["interval"] = 60
  ["expected_status"] = 200
}

Then render monitor configuration for all your services at once:

# Generate monitor configs for all services
for env_file in config/*.pkl; do
  config=$(pkl eval --format json "$env_file")
  health_url=$(echo "$config" | jq -r '.healthUrl')
  monitor_name=$(echo "$config" | jq -r '.vigilmonMonitorName')

  # Create monitor via Vigilmon API
  curl -X POST "https://vigilmon.online/api/v1/monitors" \
    -H "Authorization: Bearer $VIGILMON_API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"name\": \"$monitor_name\", \"type\": \"http\", \"url\": \"$health_url\", \"interval\": 60, \"expected_status\": 200}"

  echo "Created monitor: $monitor_name -> $health_url"
done

This approach treats your Vigilmon monitoring setup as code, derived from the same Pkl service definitions that configure your services — configuration and monitoring stay in sync automatically.


Step 3: Monitor Kubernetes services generated from Pkl manifests

Pkl is frequently used to generate Kubernetes manifests. When a Pkl-generated manifest deploys a Kubernetes Service and Ingress, Vigilmon can monitor the resulting endpoint.

Example Pkl-generated Kubernetes manifest:

// k8s/deployment.pkl
import "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.0#/K8s.pkl"

local serviceName = "user-api"
local servicePort = 8080

apiVersion = "apps/v1"
kind = "Deployment"
metadata {
  name = serviceName
  labels {
    ["app"] = serviceName
  }
}
spec {
  replicas = 3
  selector {
    matchLabels {
      ["app"] = serviceName
    }
  }
  template {
    spec {
      containers {
        new {
          name = serviceName
          image = "yourcompany/user-api:latest"
          ports {
            new { containerPort = servicePort }
          }
          readinessProbe {
            httpGet {
              path = "/healthz"
              port = servicePort
            }
            initialDelaySeconds = 10
            periodSeconds = 5
          }
        }
      }
    }
  }
}

Apply the generated manifest:

pkl eval --format yaml k8s/deployment.pkl | kubectl apply -f -

Kubernetes readiness probes check the pod from inside the cluster. Vigilmon checks the ingress-exposed URL from outside — catching ingress controller failures, DNS issues, and TLS problems that in-cluster probes miss.


Step 4: Heartbeat monitoring for Pkl generation pipelines

Pkl generation is a step in your build or deployment pipeline. If it silently stops running — due to a CI configuration change, a broken Pkl import, or a schema registry outage — services stop getting updated configuration.

Use Vigilmon heartbeat monitors to verify your Pkl pipelines are running:

  1. In Vigilmon, go to Monitors → New Monitor
  2. Choose Heartbeat
  3. Set the expected interval to match your pipeline cadence
  4. Copy the heartbeat URL

Add a ping to your CI pipeline after Pkl generation:

# .github/workflows/deploy.yml
name: Deploy with Pkl

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Pkl
        run: |
          curl -Lo /usr/local/bin/pkl \
            https://github.com/apple/pkl/releases/latest/download/pkl-linux-amd64
          chmod +x /usr/local/bin/pkl

      - name: Validate Pkl configs
        run: pkl eval config/*.pkl --format json > /dev/null

      - name: Generate Kubernetes manifests
        run: pkl eval k8s/*.pkl --format yaml > manifests/

      - name: Apply manifests
        run: kubectl apply -f manifests/

      - name: Ping Vigilmon heartbeat
        run: curl -fsS --retry 3 "https://hb.vigilmon.online/your-heartbeat-id" > /dev/null

If the Pkl validation step fails (type error, constraint violation, import resolution failure), the pipeline exits before the heartbeat ping. Vigilmon alerts you within one missed interval.


Step 5: Configure alert channels

When a Pkl-configured service goes down, your team needs immediate notification — and enough context to know whether the failure is a configuration issue or an infrastructure issue.

Slack webhook integration

  1. In Vigilmon, go to Alert Channels → Add Channel → Webhook
  2. Enter your Slack incoming webhook URL
  3. Assign the channel to your monitors

Vigilmon's downtime payload:

{
  "monitor_name": "pkl/user-api",
  "status": "down",
  "url": "https://api.yourcompany.com/healthz",
  "started_at": "2024-01-15T10:23:00Z",
  "duration_seconds": 90
}

Diagnosing Pkl-related failures when alerts fire

When Vigilmon alerts you, run this diagnostic sequence to determine whether the failure is configuration-related:

# Validate the current Pkl config
pkl eval config/production.pkl

# Check what configuration the service is actually using at runtime
curl -s https://api.yourcompany.com/debug/config | jq '.config_version'

# Compare rendered config to what's deployed
pkl eval --format json config/production.pkl > /tmp/expected.json
kubectl get configmap user-api-config -o json | jq '.data' > /tmp/actual.json
diff /tmp/expected.json /tmp/actual.json

# Check Kubernetes pod health (for k8s deployments)
kubectl get pods -l app=user-api
kubectl describe pod -l app=user-api | grep -A 5 "Conditions"

# Test the health endpoint from inside the cluster
kubectl run tmp-curl --image=curlimages/curl --rm -it --restart=Never -- \
  curl http://user-api:8080/healthz

If in-cluster health checks pass but Vigilmon shows the endpoint as down, the issue is in the ingress/network path — not the configuration. If both fail, check your Pkl configuration for recent changes that could have changed service behavior.


Step 6: SSL certificate monitoring

Services configured with Pkl often specify TLS certificate settings in the Pkl config itself. Vigilmon monitors certificate expiry automatically for HTTPS monitors:

  • 30 days before expiry — time to rotate
  • 7 days before expiry — urgent warning
  • On expiry — monitor reports down

You can encode certificate expiry alerting thresholds in your Pkl service definition:

// ServiceConfig.pkl (extended)
/// Certificate warning threshold in days
certExpiryWarnDays: Int(isBetween(7, 90)) = 30

Step 7: Status page for Pkl-managed services

Give your organization a single view of all services managed through Pkl configuration.

  1. In Vigilmon, go to Status Pages → New Status Page
  2. Name it: "Platform Services"
  3. Add monitors organized by domain or team:
    • User Platform: user-api, auth-service
    • Commerce: order-service, catalog-service
    • Data: pipeline-api, analytics-service
  4. Publish the page

Include the status page URL in your internal developer portal so teams can check service health before filing incidents.


Putting it all together

| Monitor | Type | What it catches | |---------|------|-----------------| | https://api.yourcompany.com/healthz | HTTP | App failures, config errors | | api.yourcompany.com:443 | TCP | TLS/port binding failures | | Pkl CI pipeline heartbeat | Heartbeat | Config generation failures | | https://api.yourcompany.com | SSL | Certificate expiry |


What's next

  • Configuration-as-code for monitoring — extend your Pkl schemas to include Vigilmon monitor specifications, so adding a new service automatically generates the monitoring configuration in your CI pipeline
  • Environment parity checks — monitor the same healthPath across production, staging, and development environments; diverging uptime between environments signals a configuration mismatch worth investigating
  • SLO definitions in Pkl — define availability SLO targets in your Pkl service config and cross-reference them with Vigilmon's uptime history to report on SLO compliance

Get started free at vigilmon.online — no credit card, monitors start running in under a minute.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →