Technology Encyclopedia Home >Set Up Ofelia — A Docker-Native Cron Job Scheduler with Web UI for Your Cloud Server

Set Up Ofelia — A Docker-Native Cron Job Scheduler with Web UI for Your Cloud Server

Standard cron works fine, but if you're running a Docker-based setup, Ofelia is a cleaner alternative. It's a Docker-native job scheduler that reads job definitions from Docker container labels — no crontab files, no editing shell configs. Add a label to a Docker container and Ofelia runs that container's command on your schedule.

It also includes a web UI showing job execution history, success/failure status, and output logs. If you manage a Docker Compose stack with multiple scheduled tasks, Ofelia keeps everything in one place.

I run Ofelia on Tencent Cloud Lighthouse alongside my Docker Compose stack. It's extremely lightweight — a few MB of memory. The combination of Ofelia + Lighthouse works well because the server runs continuously, which means time-triggered jobs (backups at 2am, weekly reports) execute on schedule without needing your laptop on. Lighthouse's OrcaTerm terminal also lets you check Docker logs to see Ofelia's job execution output from any browser, which is useful for verifying that scheduled tasks are running correctly.


Table of Contents

  1. Ofelia vs Standard Cron
  2. What You Need
  3. Part 1: Deploy Ofelia with Docker Compose
  4. Part 2: Define Jobs via Container Labels
  5. Part 3: Configure the Web UI
  6. Part 4: Job Types and Advanced Configuration
  7. Part 5: Access the Web UI via Nginx
  8. The Thing That Tripped Me Up
  9. Troubleshooting
  10. Summary

  • Key Takeaways
  • Use the appropriate Lighthouse application image to skip manual installation steps where available
  • Lighthouse snapshots provide one-click full-server backup before major changes
  • OrcaTerm browser terminal lets you manage the server from any device
  • CBS cloud disk expansion handles growing storage needs without server migration
  • Console-level firewall + UFW = two independent protection layers

Ofelia vs Standard Cron {#ofelia-vs-cron}

Feature Ofelia Standard Cron
Configuration Docker labels or YAML Crontab file
Job visibility Web UI with history Log files only
Docker integration Native (runs containers) Manual docker exec
Success/failure tracking Built-in Parse logs manually
Email/Slack notifications Built-in Manual scripting
Execution logs Stored and viewable Redirect to files

Use Ofelia when: you're already using Docker Compose and want job scheduling integrated with your container setup.
Stick with cron when: you have simple scripts that don't need Docker, or you prefer traditional Unix tools.


What You Need {#prerequisites}

Requirement Details
Server Ubuntu 22.04 with Docker
Docker Compose v2.x

Part 1: Deploy Ofelia with Docker Compose {#part-1}

Create or update your /opt/mystack/docker-compose.yml:

version: "3.8"

services:
  ofelia:
    image: mcuadros/ofelia:latest
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    command: daemon --docker
    labels:
      ofelia.enabled: "true"

  # Your web application
  webapp:
    image: your-app:latest
    labels:
      # Run database backup every day at 2 AM
      ofelia.enabled: "true"
      ofelia.job-exec.db-backup.schedule: "@daily"
      ofelia.job-exec.db-backup.command: "/app/scripts/backup.sh"

  # Nginx
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"

  # A dedicated container for scheduled jobs
  scheduler-tasks:
    image: alpine:latest
    command: tail -f /dev/null  # Keep container running
    volumes:
      - /opt/scripts:/scripts
    labels:
      ofelia.enabled: "true"
      
      # Task 1: Log cleanup every Sunday at 3 AM
      ofelia.job-exec.cleanup-logs.schedule: "0 3 * * 0"
      ofelia.job-exec.cleanup-logs.command: "sh -c 'find /scripts/logs -mtime +7 -delete && echo Cleanup done'"
      
      # Task 2: Health check every 5 minutes
      ofelia.job-exec.health-check.schedule: "@every 5m"
      ofelia.job-exec.health-check.command: "wget -q -O /dev/null http://webapp:3000/health"

Start the stack:

cd /opt/mystack
docker compose up -d
docker compose logs -f ofelia

Part 2: Define Jobs via Container Labels {#part-2}

Ofelia reads job definitions from container labels in this format:

ofelia.job-exec.[JOB_NAME].[SETTING]: value

Schedule Formats

# Standard cron (minute hour day month weekday)
ofelia.job-exec.myjob.schedule: "30 2 * * *"     # Every day at 2:30 AM

# Special keywords
ofelia.job-exec.myjob.schedule: "@hourly"
ofelia.job-exec.myjob.schedule: "@daily"
ofelia.job-exec.myjob.schedule: "@weekly"
ofelia.job-exec.myjob.schedule: "@monthly"

# Intervals
ofelia.job-exec.myjob.schedule: "@every 5m"
ofelia.job-exec.myjob.schedule: "@every 1h"
ofelia.job-exec.myjob.schedule: "@every 30s"

Complete Job Example

labels:
  ofelia.enabled: "true"
  
  # Job name: wordpress-backup
  ofelia.job-exec.wordpress-backup.schedule: "@daily"
  ofelia.job-exec.wordpress-backup.command: "sh /scripts/backup-wordpress.sh"
  
  # Email notification on failure
  ofelia.job-exec.wordpress-backup.on-error: "send_email"
  ofelia.job-exec.wordpress-backup.smtp-host: "smtp.sendgrid.net"
  ofelia.job-exec.wordpress-backup.smtp-port: "587"
  ofelia.job-exec.wordpress-backup.email-to: "you@example.com"
  ofelia.job-exec.wordpress-backup.email-from: "ofelia@yourdomain.com"

Part 3: Configure the Web UI {#part-3}

Ofelia's web UI runs on port 8080 by default. Enable it in the Ofelia service:

services:
  ofelia:
    image: mcuadros/ofelia:latest
    restart: always
    ports:
      - "127.0.0.1:8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    command: daemon --docker

View via SSH tunnel:

ssh -L 8080:localhost:8080 ubuntu@YOUR_SERVER_IP

Open http://localhost:8080 — you'll see:

  • All configured jobs with their schedules
  • Last execution time and status (Success/Failed)
  • Output/error logs from each execution
  • Next scheduled run time

Part 4: Job Types and Advanced Configuration {#part-4}

Job Types

job-exec (most common) — runs a command inside an existing running container:

ofelia.job-exec.cleanup.schedule: "@daily"
ofelia.job-exec.cleanup.command: "rm -rf /tmp/cache/*"

job-run — starts a new container instance to run the command:

ofelia.job-run.report.schedule: "@weekly"
ofelia.job-run.report.image: "python:3.10-slim"
ofelia.job-run.report.command: "python /opt/scripts/weekly_report.py"
ofelia.job-run.report.volume: "/opt/scripts:/opt/scripts"
ofelia.job-run.report.environment: "DB_URL=postgresql://user:pass@db/myapp"

job-local — runs a command directly on the host:

ofelia.job-local.host-backup.schedule: "@daily"
ofelia.job-local.host-backup.command: "tar -czf /backups/data-$(date +%Y%m%d).tar.gz /opt/data"

YAML Configuration (Alternative to Labels)

For complex setups, use a YAML config file:

nano /opt/mystack/ofelia.yaml
[job-exec "db-backup"]
schedule = @daily
container = mystack-db-1
command = /scripts/backup.sh

[job-exec "log-cleanup"]
schedule = 0 3 * * 0
container = mystack-webapp-1
command = find /app/logs -mtime +7 -delete

[job-run "weekly-report"]
schedule = @weekly
image = python:3.10-slim
command = python /opt/report.py
volume = /opt/scripts:/opt

Update Ofelia to use the config file:

services:
  ofelia:
    image: mcuadros/ofelia:latest
    command: daemon --config=/etc/ofelia/config.yaml
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /opt/mystack/ofelia.yaml:/etc/ofelia/config.yaml

Part 5: Access the Web UI via Nginx {#part-5}

Expose the web UI securely:

sudo nano /etc/nginx/sites-available/ofelia
server {
    listen 80;
    server_name jobs.yourdomain.com;

    auth_basic "Ofelia Job Scheduler";
    auth_basic_user_file /etc/nginx/.htpasswd;

    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
sudo htpasswd -c /etc/nginx/.htpasswd admin
sudo ln -s /etc/nginx/sites-available/ofelia /etc/nginx/sites-enabled/
sudo certbot --nginx -d jobs.yourdomain.com

The Thing That Tripped Me Up {#gotcha}

My Ofelia jobs were failing silently. The web UI showed them as "Success" but the commands weren't doing anything — no files created, no logs written.

The issue: I was using job-exec to run commands in a container, but the container I was targeting had a different actual name than what I specified.

Docker Compose names containers as projectname-servicename-1 (or with variations). My label said container = myapp but the actual container name was mystack-myapp-1.

How to find the actual container name:

docker ps --format "{{.Names}}"
# Shows: mystack-webapp-1, mystack-db-1, mystack-ofelia-1

The fix: Match the container name exactly:

labels:
  ofelia.job-exec.backup.container: "mystack-webapp-1"  # Exact name from docker ps

Or use a job-local job to avoid container name confusion when running scripts on the host.

Also: Ofelia needs to be restarted after changing container labels:

docker compose restart ofelia

Troubleshooting {#troubleshooting}

Issue Likely Cause Fix
Jobs not appearing Labels not picked up Check docker compose logs ofelia; restart Ofelia
"Container not found" Wrong container name Run docker ps --format "{{.Names}}" to find exact name
Job shows "Success" but did nothing Script error silently caught Check command directly: docker exec CONTAINER your-command
Web UI empty Port not mapped Add ports: - "127.0.0.1:8080:8080" to Ofelia service
Schedule not matching Timezone mismatch Set TZ=America/New_York in Ofelia environment
Email notifications not sent SMTP config Test SMTP separately; check Ofelia logs for auth errors

Summary {#verdict}

What you built:

  • Ofelia job scheduler integrated with your Docker Compose stack
  • Jobs defined as container labels — no external config files
  • Web UI for job history, status, and execution logs
  • Multiple job types: exec (in running containers), run (new container), local (host)
  • Email/Slack notifications on job failure
  • HTTPS web UI via Nginx

Ofelia makes scheduled jobs first-class citizens in your Docker setup. Adding a new scheduled task is as simple as adding two label lines to a container definition.

Frequently Asked Questions {#faq}

What's the difference between Ofelia and built-in Linux cron?
Linux cron runs commands on a schedule. Ofelia provides additional capabilities like visual management, dependency handling, error notifications, and often Docker-native integration.

How do I debug a failing Ofelia task?
Check the execution logs first. Verify the command works when run manually with the same user/environment. Common issues: incorrect paths, missing environment variables, permission problems.

How do I make Ofelia tasks resilient to failures?
Implement retry logic, alert on failures (email/Slack notification), and log output to a file. For critical tasks, consider writing a simple success/failure status to a monitoring endpoint.

What happens if the server restarts — do scheduled tasks continue?
If configured as a systemd service (as shown in this guide), Ofelia restarts automatically on server reboot and resumes its schedule. Configure Restart=on-failure for crash recovery.

How do I monitor task execution history?
Ofelia typically provides logs via systemctl or its own web interface. For important tasks, write output to a log file and use a monitoring tool to check for failures.

👉 Get started with Tencent Cloud Lighthouse
👉 View current pricing and launch promotions
👉 Explore all active deals and offers