Healthchecks is a cron job monitoring service that listens for HTTP requests and emails you when your cron jobs don’t arrive on time. It’s a simple but effective way to ensure your scheduled tasks are running as expected.
| File/Directory | Path | Purpose |
|---|---|---|
| Main config | /etc/healthchecks/config.py |
Main Healthchecks configuration |
| Local config | /etc/healthchecks/local_settings.py |
Local overrides |
| Database | /var/lib/healthchecks/healthchecks.db |
SQLite database |
| Static files | /var/lib/healthchecks/static/ |
Static web assets |
| Templates | /var/lib/healthchecks/templates/ |
Email templates |
| Logs | /var/log/healthchecks/ |
Log files |
| Systemd service | /etc/systemd/system/healthchecks.service |
Service definition |
| Web config | /etc/nginx/sites-available/healthchecks |
Nginx configuration |
| Environment | /etc/healthchecks/.env |
Environment variables |
# /etc/healthchecks/config.py
# ========================
# Django Settings
# ========================
# Build paths inside the project
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'your-super-secret-key-change-this-in-production'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
# Allowed hosts
ALLOWED_HOSTS = ['healthchecks.example.com', 'localhost', '127.0.0.1']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.humanize',
'compressor',
'corsheaders',
'healthchecks',
'api',
'hc.accounts',
'hc.api',
'hc.front',
'hc.payments',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'hc.accounts.middleware.TeamAccessMiddleware',
]
ROOT_URLCONF = 'hc.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'hc.front.context_processors.branding',
],
},
},
]
WSGI_APPLICATION = 'hc.wsgi.application'
# ========================
# Database Settings
# ========================
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'healthchecks.db'),
}
}
# PostgreSQL configuration (for production)
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.postgresql',
# 'NAME': 'healthchecks',
# 'USER': 'healthchecks',
# 'PASSWORD': 'DbPassword123!',
# 'HOST': 'localhost',
# 'PORT': '5432',
# 'CONN_MAX_AGE': 600,
# }
# }
# ========================
# Password Validation
# ========================
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {
'min_length': 8,
}
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# ========================
# Internationalization
# ========================
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Available languages
LANGUAGES = [
('en', 'English'),
('es', 'Spanish'),
('fr', 'French'),
('de', 'German'),
('it', 'Italian'),
('pt', 'Portuguese'),
('ru', 'Russian'),
('zh', 'Chinese'),
('ja', 'Japanese'),
]
# ========================
# Static Files
# ========================
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
'compressor.finders.CompressorFinder',
)
COMPRESS_CSS_FILTERS = [
'compressor.filters.css_default.CssAbsoluteFilter',
'compressor.filters.cssmin.CSSMinFilter'
]
# ========================
# Email Settings
# ========================
# Email backend
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# SMTP settings
EMAIL_HOST = 'smtp.example.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'healthchecks@example.com'
EMAIL_HOST_PASSWORD = 'EmailPassword123!'
EMAIL_USE_TLS = True
EMAIL_USE_SSL = False
# Default from address
DEFAULT_FROM_EMAIL = 'healthchecks@example.com'
SERVER_EMAIL = 'healthchecks@example.com'
# Email timeout
EMAIL_TIMEOUT = 10
# ========================
# Site Settings
# ========================
# Site name
SITE_NAME = 'Healthchecks'
SITE_NAME_LONG = 'Healthchecks - Cron Job Monitoring'
# Site root URL
SITE_ROOT = 'https://healthchecks.example.com'
# Site logo
SITE_LOGO_URL = '/static/img/logo.svg'
# Master badge URL
MASTER_BADGE_URL = SITE_ROOT + '/badge/master/{uuid}.svg'
# ========================
# Registration Settings
# ========================
# Enable/disable registration
REGISTRATION_OPEN = True
# Require email verification
REGISTRATION_VERIFY_EMAIL = True
# Email verification expiry (hours)
EMAIL_VERIFICATION_EXPIRY_HOURS = 48
# Enable/disable password reset
PASSWORD_RESET_ENABLED = True
# Password reset expiry (hours)
PASSWORD_RESET_EXPIRY_HOURS = 24
# Enable/disable team creation
TEAMS_ENABLED = True
# Maximum checks per account
MAX_CHECKS_PER_ACCOUNT = 100
# Maximum checks per team
MAX_CHECKS_PER_TEAM = 500
# ========================
# Check Settings
# ========================
# Default check timeout (seconds)
DEFAULT_TIMEOUT = 3600
# Default check grace time (seconds)
DEFAULT_GRACE = 3600
# Minimum check timeout (seconds)
MIN_TIMEOUT = 60
# Maximum check timeout (seconds)
MAX_TIMEOUT = 31536000 # 1 year
# Minimum grace time (seconds)
MIN_GRACE = 60
# Maximum grace time (seconds)
MAX_GRACE = 31536000 # 1 year
# Default check schedule (cron expression)
DEFAULT_SCHEDULE = '0 0 * * *'
# Supported schedule formats
SUPPORTED_SCHEDULES = ['cron', 'simple', 'utc']
# ========================
# Notification Settings
# ========================
# Enable/disable email notifications
EMAIL_ENABLED = True
# Email notifications from address
EMAIL_FROM = 'healthchecks@example.com'
# Enable/disable Slack notifications
SLACK_ENABLED = True
SLACK_CLIENT_ID = 'your-slack-client-id'
SLACK_CLIENT_SECRET = 'your-slack-client-secret'
# Enable/disable Discord notifications
DISCORD_ENABLED = True
# Enable/disable Microsoft Teams notifications
MS_TEAMS_ENABLED = True
# Enable/disable Telegram notifications
TELEGRAM_ENABLED = True
TELEGRAM_BOT_NAME = 'your-telegram-bot'
TELEGRAM_TOKEN = 'your-telegram-token'
# Enable/disable Pushover notifications
PUSHOVER_ENABLED = True
PUSHOVER_API_TOKEN = 'your-pushover-token'
PUSHOVER_SUBSCRIPTION_URL = 'https://pushover.net/subscribe/your-app'
# Enable/disable PagerDuty notifications
PAGERDUTY_ENABLED = True
# Enable/disable Opsgenie notifications
OPSGENIE_ENABLED = True
OPSGENIE_API_KEY = 'your-opsgenie-api-key'
# Enable/disable VictorOps notifications
VICTOROPS_ENABLED = True
VICTOROPS_API_KEY = 'your-victorops-api-key'
# Enable/disable Pushbullet notifications
PUSHBULLET_ENABLED = True
PUSHBULLET_API_KEY = 'your-pushbullet-api-key'
# Enable/disable SMS notifications (via Twilio)
TWILIO_ENABLED = False
TWILIO_ACCOUNT_SID = 'your-twilio-account-sid'
TWILIO_AUTH_TOKEN = 'your-twilio-auth-token'
TWILIO_FROM_NUMBER = '+1234567890'
# Enable/disable WhatsApp notifications (via Twilio)
TWILIO_WHATSAPP_ENABLED = False
TWILIO_WHATSAPP_FROM_NUMBER = 'whatsapp:+14155238886'
# Enable/disable Signal notifications
SIGNAL_ENABLED = False
SIGNAL_WEBHOOK_URL = ''
# Enable/disable Matrix notifications
MATRIX_ENABLED = False
MATRIX_HOMESERVER = 'https://matrix.org'
MATRIX_BOT_USERNAME = '@healthchecks:matrix.org'
MATRIX_BOT_PASSWORD = ''
# Enable/disable Apprise notifications
APPRISE_ENABLED = False
# ========================
# Webhook Settings
# ========================
# Enable/disable webhooks
WEBHOOKS_ENABLED = True
# Webhook timeout (seconds)
WEBHOOK_TIMEOUT = 10
# Maximum webhook body size (bytes)
WEBHOOK_MAX_BODY_SIZE = 1048576 # 1MB
# Webhook user agent
WEBHOOK_USER_AGENT = 'Healthchecks.io'
# ========================
# API Settings
# ========================
# Enable/disable API
API_ENABLED = True
# API rate limiting
API_RATE_LIMIT = '100/hour'
# API CORS settings
CORS_ORIGIN_ALLOW_ALL = False
CORS_ORIGIN_WHITELIST = [
'https://healthchecks.example.com',
]
# ========================
# Security Settings
# ========================
# CSRF settings
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True
CSRF_COOKIE_SAMESITE = 'Lax'
# Session settings
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
SESSION_COOKIE_AGE = 1209600 # 2 weeks
# X-Frame-Options
X_FRAME_OPTIONS = 'DENY'
# Content Security Policy
CSP_DEFAULT_SRC = ("'self'",)
CSP_SCRIPT_SRC = ("'self'", "'unsafe-inline'")
CSP_STYLE_SRC = ("'self'", "'unsafe-inline'")
CSP_IMG_SRC = ("'self'", 'data:', 'https:')
# HSTS
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
# SSL redirect
SECURE_SSL_REDIRECT = True
# Proxy settings (for reverse proxy)
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
USE_X_FORWARDED_HOST = True
USE_X_FORWARDED_PORT = True
# ========================
# Logging
# ========================
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {message}',
'style': '{',
},
},
'handlers': {
'file': {
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': '/var/log/healthchecks/healthchecks.log',
'formatter': 'verbose',
},
'console': {
'level': 'INFO',
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
},
'loggers': {
'django': {
'handlers': ['file', 'console'],
'level': 'INFO',
'propagate': True,
},
'hc': {
'handlers': ['file', 'console'],
'level': 'INFO',
'propagate': True,
},
},
}
# ========================
# Performance Settings
# ========================
# Cache settings
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
}
}
# Redis cache (for production)
# CACHES = {
# 'default': {
# 'BACKEND': 'django.core.cache.backends.redis.RedisCache',
# 'LOCATION': 'redis://localhost:6379/1',
# }
# }
# Session cache
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'
# ========================
# Integration Settings
# ========================
# Enable/disable integrations page
INTEGRATIONS_PAGE_ENABLED = True
# Integration documentation URLs
INTEGRATION_DOCS = {
'shell': 'https://healthchecks.io/docs/cron_cli/',
'python': 'https://healthchecks.io/docs/python/',
'php': 'https://healthchecks.io/docs/php/',
'node': 'https://healthchecks.io/docs/node/',
'go': 'https://healthchecks.io/docs/go/',
}
# ========================
# Payment Settings (Optional)
# ========================
# Enable/disable payments
PAYMENTS_ENABLED = False
# Stripe settings
STRIPE_PUBLIC_KEY = ''
STRIPE_SECRET_KEY = ''
STRIPE_ENDPOINT_SECRET = ''
# Plan pricing
PLANS = {
'free': {
'name': 'Free',
'price': 0,
'checks': 20,
'grace': 6 * 3600,
'team_size': 1,
},
'plus': {
'name': 'Plus',
'price': 500, # cents
'checks': 100,
'grace': 24 * 3600,
'team_size': 5,
},
'business': {
'name': 'Business',
'price': 2000, # cents
'checks': 1000,
'grace': 72 * 3600,
'team_size': 20,
},
}
# ========================
# Customization
# ========================
# Custom CSS
CUSTOM_CSS = ''
# Custom JavaScript
CUSTOM_JS = ''
# Custom footer text
FOOTER_TEXT = ''
# Privacy policy URL
PRIVACY_URL = '/privacy/'
# Terms of service URL
TERMS_URL = '/terms/'
# Contact email
CONTACT_EMAIL = 'support@example.com'
# Support URL
SUPPORT_URL = 'mailto:support@example.com'
# /etc/healthchecks/local_settings.py
# Local settings override config.py settings
# Debug mode (disable in production)
DEBUG = False
# Allowed hosts
ALLOWED_HOSTS = ['healthchecks.example.com']
# Database
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'healthchecks',
'USER': 'healthchecks',
'PASSWORD': 'SecureDbPassword123!',
'HOST': 'localhost',
'PORT': '5432',
'CONN_MAX_AGE': 600,
}
}
# Secret key
SECRET_KEY = 'your-production-secret-key'
# Email settings
EMAIL_HOST = 'smtp.example.com'
EMAIL_HOST_USER = 'healthchecks@example.com'
EMAIL_HOST_PASSWORD = 'SecureEmailPassword123!'
# Site settings
SITE_ROOT = 'https://healthchecks.example.com'
# Telegram settings
TELEGRAM_BOT_NAME = 'your_healthchecks_bot'
TELEGRAM_TOKEN = 'your-telegram-bot-token'
# Cache
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://localhost:6379/1',
}
}
# /etc/healthchecks/.env
# Django settings
SECRET_KEY=your-super-secret-key
DEBUG=False
ALLOWED_HOSTS=healthchecks.example.com,localhost
# Database
DB_ENGINE=django.db.backends.postgresql
DB_NAME=healthchecks
DB_USER=healthchecks
DB_PASSWORD=SecureDbPassword123!
DB_HOST=localhost
DB_PORT=5432
# Email
EMAIL_HOST=smtp.example.com
EMAIL_PORT=587
EMAIL_HOST_USER=healthchecks@example.com
EMAIL_HOST_PASSWORD=SecureEmailPassword123!
EMAIL_USE_TLS=True
DEFAULT_FROM_EMAIL=healthchecks@example.com
# Site settings
SITE_ROOT=https://healthchecks.example.com
SITE_NAME=Healthchecks
# Telegram
TELEGRAM_BOT_NAME=your_healthchecks_bot
TELEGRAM_TOKEN=your-telegram-token
# Slack
SLACK_CLIENT_ID=your-slack-client-id
SLACK_CLIENT_SECRET=your-slack-client-secret
# Pushover
PUSHOVER_API_TOKEN=your-pushover-token
# Cache
CACHE_LOCATION=redis://localhost:6379/1
# Security
CSRF_COOKIE_SECURE=True
SESSION_COOKIE_SECURE=True
SECURE_SSL_REDIRECT=True
# /etc/nginx/sites-available/healthchecks
server {
listen 80;
server_name healthchecks.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name healthchecks.example.com;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/healthchecks.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/healthchecks.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Logging
access_log /var/log/nginx/healthchecks.access.log;
error_log /var/log/nginx/healthchecks.error.log;
# Static files
location /static/ {
alias /var/lib/healthchecks/static/;
expires 30d;
add_header Cache-Control "public, immutable";
}
# Media files
location /media/ {
alias /var/lib/healthchecks/media/;
expires 7d;
}
# Django application
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $server_name;
proxy_redirect off;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# Buffering
proxy_buffering off;
}
# Health check endpoint
location /health/ {
access_log off;
return 200 "OK\n";
add_header Content-Type text/plain;
}
}
<!-- /var/lib/healthchecks/templates/emails/alert.html -->
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; }
.alert-critical { background-color: #ff4444; color: white; padding: 10px; }
.alert-warning { background-color: #ffbb33; color: black; padding: 10px; }
.info { background-color: #f5f5f5; padding: 15px; margin: 10px 0; }
.button { background-color: #007bff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; }
</style>
</head>
<body>
<div class="alert-{{ check.status }}">
<h2>{{ check.name }} - {{ check.status|upper }}</h2>
</div>
<div class="info">
<p><strong>Check:</strong> {{ check.name }}</p>
<p><strong>Project:</strong> {{ project.name }}</p>
<p><strong>Time:</strong> {{ check.last_ping|date:"Y-m-d H:i:s" }}</p>
<p><strong>Period:</strong> {{ check.timeout }} seconds</p>
</div>
<p>
<a href="{{ site_root }}/checks/{{ check.code }}" class="button">View Check</a>
</p>
<hr>
<p style="font-size: 12px; color: #666;">
This alert was sent by {{ site_name }}.<br>
To unsubscribe, visit your notification settings.
</p>
</body>
</html>
# Custom webhook configuration
WEBHOOK_INTEGRATIONS = {
'custom_api': {
'url': 'https://api.example.com/healthchecks/alert',
'method': 'POST',
'headers': {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_TOKEN'
},
'body': '''
{
"check_name": "{{ check.name }}",
"check_code": "{{ check.code }}",
"status": "{{ check.status }}",
"project": "{{ project.name }}",
"last_ping": "{{ check.last_ping|date:'c' }}",
"next_ping": "{{ check.next_ping|date:'c' }}"
}
'''
}
}
# Change to Healthchecks directory
cd /var/lib/healthchecks
# Validate Django configuration
python3 manage.py check
# Check database connection
python3 manage.py dbshell <<< "SELECT 1;"
# Test email configuration
python3 manage.py sendtestemail admin@example.com
# Collect static files
python3 manage.py collectstatic --noinput
# Change to Healthchecks directory
cd /var/lib/healthchecks
# Run migrations
python3 manage.py migrate
# Create superuser
python3 manage.py createsuperuser
# Create default checks (optional)
python3 manage.py createchecks
# Backup database
sqlite3 /var/lib/healthchecks/healthchecks.db ".backup '/var/backups/healthchecks/backup.db'"
# Restore database
sqlite3 /var/lib/healthchecks/healthchecks.db ".restore '/var/backups/healthchecks/backup.db'"
# Restart Healthchecks service
sudo systemctl restart healthchecks
# Check service status
sudo systemctl status healthchecks
# View logs
sudo journalctl -u healthchecks -f
sudo tail -f /var/log/healthchecks/healthchecks.log
# Restart Nginx
sudo systemctl restart nginx
# Restart Gunicorn (if using)
sudo systemctl restart gunicorn-healthchecks
# Check service status
sudo systemctl status healthchecks
# Check if listening
sudo netstat -tlnp | grep 8000
# Check running processes
ps aux | grep healthchecks
# Access web interface
curl http://localhost:8000
# Check health endpoint
curl http://localhost:8000/health/
# Check API endpoint
curl http://localhost:8000/api/v1/checks/
# Check login page
curl http://localhost:8000/accounts/login/
# Send test email
python3 /var/lib/healthchecks/manage.py sendtestemail admin@example.com
# Check email logs
tail -f /var/log/healthchecks/healthchecks.log | grep email
# Test webhook integration
curl -X POST https://healthchecks.example.com/ping/{uuid}
# Check integration status
python3 /var/lib/healthchecks/manage.py shell -c "from hc.api.models import Check; print(Check.objects.count())"
Every deployment is unique. We provide consulting for:
Get personalized assistance: office@linux-server-admin.com | Contact Page