Watchdog can be run in several ways depending on your deployment needs. This guide covers running in development, building production binaries, and deployment considerations.
Running with go run
The fastest way to start Watchdog during development is using go run.
Start the monitoring service
Or use the short alias:
What happens when you start the service
Environment initialization
Watchdog loads configuration from environment variables (.env file):
Database connection settings
Redis connection settings
Worker pool configuration
SMTP settings for notifications
Database and Redis connections
The service establishes connections: Connected to PostgreSQL database!
Redis is pinged to verify connectivity. If either connection fails, the service will panic with an error message.
Orchestrator initialization
The orchestrator bootstraps the monitoring system:
Creates the event bus for pub/sub messaging
Registers event listeners for persistence and notifications
Instantiates the Supervisor for check evaluation
Creates ParentWorker instances for each monitoring interval
Service is running
The service now actively monitors all configured URLs according to their frequencies.
Development workflow
# Terminal 1: Run the service
go run ./cmd/... guard
# Terminal 2: Add URLs to monitor
go run ./cmd/... add https://example.com get five_minutes [email protected]
# Terminal 3: List monitored URLs
go run ./cmd/... list
When running with go run, the service will recompile each time you run it. For faster startup, consider building a binary (see below).
Building production binaries
For production deployments, build a standalone binary instead of using go run.
Build the binary
go build -o watchdog ./cmd/...
This creates a watchdog binary in your current directory.
Build with optimizations
For production, use build flags to optimize the binary:
# Remove debug information and reduce binary size
go build -ldflags= "-s -w" -o watchdog ./cmd/...
# Build for Linux (amd64)
GOOS = linux GOARCH = amd64 go build -o watchdog-linux ./cmd/...
# Build for Linux (arm64)
GOOS = linux GOARCH = arm64 go build -o watchdog-arm ./cmd/...
# Build for macOS (Intel)
GOOS = darwin GOARCH = amd64 go build -o watchdog-macos ./cmd/...
# Build for macOS (Apple Silicon)
GOOS = darwin GOARCH = arm64 go build -o watchdog-macos-arm ./cmd/...
# Build for Windows
GOOS = windows GOARCH = amd64 go build -o watchdog.exe ./cmd/...
Running the binary
Once built, run the binary directly:
# Start the service
./watchdog guard
# Add a URL
./watchdog add https://example.com get five_minutes [email protected]
# List URLs
./watchdog list
# Remove a URL
./watchdog remove 42
Store the binary in /usr/local/bin or another directory in your $PATH to run it from anywhere: sudo cp watchdog /usr/local/bin/
watchdog guard
Running as a systemd service
For production Linux deployments, run Watchdog as a systemd service for automatic restarts and proper lifecycle management.
Create a systemd service file
Create /etc/systemd/system/watchdog.service:
[Unit]
Description =Watchdog Uptime Monitoring Service
After =network.target postgresql.service redis.service
Wants =postgresql.service redis.service
[Service]
Type =simple
User =watchdog
Group =watchdog
WorkingDirectory =/opt/watchdog
EnvironmentFile =/opt/watchdog/.env
ExecStart =/opt/watchdog/watchdog guard
Restart =always
RestartSec =10
StandardOutput =journal
StandardError =journal
# Security hardening
NoNewPrivileges =true
PrivateTmp =true
ProtectSystem =strict
ProtectHome =true
ReadWritePaths =/opt/watchdog
[Install]
WantedBy =multi-user.target
Set up the service
Create a dedicated user
sudo useradd -r -s /bin/ false watchdog
Create application directory
sudo mkdir -p /opt/watchdog
sudo chown watchdog:watchdog /opt/watchdog
Deploy the binary and configuration
sudo cp watchdog /opt/watchdog/
sudo cp .env /opt/watchdog/
sudo chown watchdog:watchdog /opt/watchdog/watchdog
sudo chown watchdog:watchdog /opt/watchdog/.env
sudo chmod 600 /opt/watchdog/.env
Enable and start the service
# Reload systemd
sudo systemctl daemon-reload
# Enable service to start on boot
sudo systemctl enable watchdog
# Start the service
sudo systemctl start watchdog
Verify the service is running
sudo systemctl status watchdog
Expected output: ● watchdog.service - Watchdog Uptime Monitoring Service
Loaded: loaded (/etc/systemd/system/watchdog.service; enabled)
Active: active (running) since Mon 2026-03-03 10:00:00 UTC
Main PID: 12345 (watchdog)
Status: "Watchdog is running"
Managing the systemd service
# Start the service
sudo systemctl start watchdog
# Stop the service
sudo systemctl stop watchdog
# Restart the service
sudo systemctl restart watchdog
# Check status
sudo systemctl status watchdog
# View logs
sudo journalctl -u watchdog -f
# View logs from the last hour
sudo journalctl -u watchdog --since "1 hour ago"
Running in Docker
While the repository doesn’t include a Dockerfile, you can create one for containerized deployments.
Create a Dockerfile
Create Dockerfile in your project root:
# Build stage
FROM golang:1.21-alpine AS builder
WORKDIR /app
# Copy go mod files
COPY go.mod go.sum ./
RUN go mod download
# Copy source code
COPY . .
# Build binary
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags= "-s -w" -o watchdog ./cmd/...
# Runtime stage
FROM alpine:latest
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /app
# Copy binary from builder
COPY --from=builder /app/watchdog .
# Create non-root user
RUN addgroup -g 1000 watchdog && \
adduser -D -u 1000 -G watchdog watchdog && \
chown -R watchdog:watchdog /app
USER watchdog
CMD [ "./watchdog" , "guard" ]
Build the Docker image
docker build -t watchdog:latest .
Run with Docker
docker run -d \
--name watchdog \
--env-file .env \
--restart unless-stopped \
watchdog:latest
Run with docker-compose
Create docker-compose.yml:
version : '3.8'
services :
postgres :
image : timescale/timescaledb:latest-pg14
environment :
POSTGRES_USER : tsdbadmin
POSTGRES_PASSWORD : ${DB_PASSWORD}
POSTGRES_DB : tsdb
volumes :
- postgres_data:/var/lib/postgresql/data
ports :
- "5432:5432"
redis :
image : redis:7-alpine
ports :
- "6379:6379"
volumes :
- redis_data:/data
watchdog :
build : .
depends_on :
- postgres
- redis
env_file :
- .env
restart : unless-stopped
volumes :
postgres_data :
redis_data :
Start all services:
Execute commands in Docker
# Add a URL
docker exec watchdog ./watchdog add https://example.com get five_minutes [email protected]
# List URLs
docker exec watchdog ./watchdog list
# Remove a URL
docker exec watchdog ./watchdog remove 42
# View logs
docker logs -f watchdog
Environment configuration
Before running Watchdog, configure your environment variables.
Create .env file
Copy the example file:
Edit .env with your configuration:
# Application
APP_ENV = production
# Redis
REDIS_HOST = 127.0.0.1:6379
REDIS_PASS = ''
REDIS_DB = 2
# Worker Configuration
MAXIMUM_CHILD_WORKERS = 5
MAXIMUM_WORK_POOL_SIZE = 25
HTTP_REQUEST_TIMEOUT = 5
SUPERVISOR_POOL_FLUSH_TIMEOUT = 5
SUPERVISOR_POOL_FLUSH_BATCHSIZE = 100
# Database (TimescaleDB/Postgres)
DB_USER = tsdbadmin
DB_PASSWORD = your_secure_password
DB_HOST = localhost
DB_PORT = 5432
DB_DATABASE = tsdb
# Goose Migration
GOOSE_DRIVER = postgres
GOOSE_DBSTRING = "postgresql://${ DB_USER }:${ DB_PASSWORD }@${ DB_HOST }:${ DB_PORT }/${ DB_DATABASE }?sslmode=disable"
GOOSE_MIGRATION_DIR = migrations
# SMTP for Notifications
MAIL_FROM_ADDRESS = "[email protected] "
MAIL_HOST = smtp.example.com
MAIL_PORT = 587
MAIL_USERNAME = your_smtp_username
MAIL_PASSWORD = your_smtp_password
Never commit your .env file to version control. It contains sensitive credentials. The .env file is already in .gitignore.
Running database migrations
Before first run, apply database migrations to create the required tables.
Install Goose
go install github.com/pressly/goose/v3/cmd/goose@latest
Run migrations
# Navigate to migrations directory
cd migrations
# Run migrations
goose postgres "postgresql://tsdbadmin:password@localhost:5432/tsdb?sslmode=disable" up
Or use environment variables:
export GOOSE_DRIVER = postgres
export GOOSE_DBSTRING = "postgresql://tsdbadmin:password@localhost:5432/tsdb?sslmode=disable"
goose -dir migrations up
Verify migrations
goose -dir migrations status
Expected output:
2025/11/15 11:21:01 Applied At Migration
2025/11/15 11:21:01 =======================================
2025/11/15 11:21:01 Mon Mar 03 10:00:00 2026 -- create_urls_table.sql
2025/11/15 11:21:01 Mon Mar 03 10:00:01 2026 -- create_url_status_table.sql
2025/11/15 11:21:01 Mon Mar 03 10:00:02 2026 -- create_incidents_table.sql
Ensure TimescaleDB extension is enabled in your database: CREATE EXTENSION IF NOT EXISTS timescaledb;
Worker configuration
Adjust worker settings in .env based on your monitoring scale:
# Maximum child workers per parent worker
MAXIMUM_CHILD_WORKERS = 5
# Total concurrent work pool size
MAXIMUM_WORK_POOL_SIZE = 25
# HTTP request timeout (seconds)
HTTP_REQUEST_TIMEOUT = 5
Scaling guidelines:
For 10-50 URLs: Default settings work well
For 50-200 URLs: Increase MAXIMUM_WORK_POOL_SIZE to 50
For 200+ URLs: Increase to 100+ and monitor resource usage
Supervisor configuration
# Flush timeout for batch operations (seconds)
SUPERVISOR_POOL_FLUSH_TIMEOUT = 5
# Batch size for supervisor flush operations
SUPERVISOR_POOL_FLUSH_BATCHSIZE = 100
Health checks
Monitor your Watchdog service health:
Check database connectivity
# From the application
go run ./cmd/... guard
# Look for: "Connected to PostgreSQL database!"
Check Redis connectivity
The service will panic if Redis is unavailable:
Redis connection failed
panic: Redis connection failed
Monitor logs
Watchdog uses structured logging (log/slog):
# With systemd
sudo journalctl -u watchdog -f
# With Docker
docker logs -f watchdog
# Direct execution (logs to stdout)
./watchdog guard
Graceful shutdown
Stop the service
# With systemd
sudo systemctl stop watchdog
# With Docker
docker stop watchdog
# Direct execution
# Press Ctrl+C
The service will complete in-flight checks before shutting down.
Next steps
Monitor URLs Learn how to add and manage monitored URLs
Troubleshooting Resolve common issues and errors