Skip to content

Cronicle

Multi-server task scheduler and cron replacement with a web-based UI. Cronicle lets you create and manage scheduled jobs from a browser — set cron expressions, set concurrency limits, view live logs, receive email alerts, and review job history. Job data and state are stored in flat JSON files on a persistent volume.

Key Features

  • Web UI — browser-based job management with live log streaming
  • Cron replacement — cron-compatible scheduling with visual editor
  • Email notifications — SMTP alerts for job success, failure, and warnings
  • Session encryption — auto-generated secret_key stored in a Kubernetes Secret
  • Job memory limits — configurable per-job memory cap
  • Concurrency control — configurable maximum concurrent jobs per server
  • Persistent storage — PVC for job data, history, and state JSON files

Security Scan

Framework Score
MITRE + NSA + SOC2 74.24%

Security posture: acceptable.

Installation

HTTPS repository:

helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install cronicle helmforge/cronicle

OCI registry:

helm install cronicle oci://ghcr.io/helmforgedev/helm/cronicle

Deployment Examples

# values.yaml — Cronicle with public URL and persistence
cronicle:
  baseUrl: 'https://cronicle.example.com'

persistence:
  enabled: true
  size: 5Gi

ingress:
  enabled: true
  ingressClassName: traefik
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: cronicle.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: cronicle-tls
      hosts:
        - cronicle.example.com
# values.yaml — Cronicle with SMTP notifications
cronicle:
  baseUrl: 'https://cronicle.example.com'
  emailFrom: '[email protected]'
  smtpHostname: 'smtp.example.com'
  extraEnv:
    # SMTP port (default is 25; use 587 for STARTTLS)
    - name: CRONICLE_smtp_port
      value: '587'
    # Cronicle nested mail_options.secure flag
    - name: CRONICLE_mail_options__secure
      value: '0'

persistence:
  enabled: true
  size: 5Gi

ingress:
  enabled: true
  ingressClassName: traefik
  hosts:
    - host: cronicle.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: cronicle-tls
      hosts:
        - cronicle.example.com

Port 587 is commonly STARTTLS. Keep CRONICLE_mail_options__secure set to '0' unless your SMTP provider requires implicit TLS, usually on port 465.

# values.yaml — Production setup with job limits and existing secret
cronicle:
  baseUrl: 'https://cronicle.example.com'
  emailFrom: '[email protected]'
  smtpHostname: 'smtp.example.com'
  maxJobs: 4
  jobMemoryMax: 268435456 # 256 MiB per job

secret:
  create: false
  existingSecret: cronicle-secret # must contain key: secret_key

resources:
  requests:
    memory: 512Mi
    cpu: 250m
  limits:
    memory: 2Gi
    cpu: '2'

persistence:
  enabled: true
  size: 10Gi

ingress:
  enabled: true
  ingressClassName: traefik
  hosts:
    - host: cronicle.example.com
      paths:
        - path: /
          pathType: Prefix

The chart repository also includes copy-paste values files for these scenarios:

  • examples/simple.yaml - private scheduler with port-forward access.
  • examples/ingress.yaml - HTTPS ingress with bounded scheduler resources.
  • examples/notifications.yaml - SMTP notification settings.

Architecture and operations details are maintained with the chart in DESIGN.md, docs/architecture.md, and docs/operations.md.

Configuration Reference

Core

Parameter Type Default Description
nameOverride string "" Override the chart name.
fullnameOverride string "" Override the full release name.
commonLabels object {} Extra labels added to all resources.

Image

Parameter Type Default Description
image.repository string docker.io/soulteary/cronicle Cronicle container image.
image.tag string "0.9.80" Image tag.
image.pullPolicy string IfNotPresent Image pull policy.
imagePullSecrets array [] Pull secrets for private registries.

Cronicle Configuration

Parameter Type Default Description
cronicle.port integer 3012 Internal HTTP port for the Cronicle web UI.
cronicle.discoveryPort integer 3014 UDP auto-discovery port for multi-server setups.
cronicle.discoveryEnabled boolean false Enable UDP auto-discovery Service port.
cronicle.baseUrl string http://localhost:3012 Public base URL. Must be updated for notifications and UI links to work.
cronicle.emailFrom string cronicle@localhost Email sender address for job notifications.
cronicle.smtpHostname string localhost SMTP server hostname for notifications.
cronicle.jobMemoryMax integer 1073741824 Maximum memory per job in bytes (default 1 GB = 1073741824).
cronicle.maxJobs integer 0 Maximum concurrent jobs. 0 means unlimited.
cronicle.extraEnv array [] Extra environment variables for SMTP auth and advanced configuration.
baseUrl defaults to localhost — update before deploying

The default cronicle.baseUrl is http://localhost:3012. If not changed, email notification links and some internal UI references will point to localhost instead of your actual instance. Set this to your public URL (e.g. https://cronicle.example.com) before first deployment.

Limit maxJobs in production to avoid node overload

With maxJobs: 0 (unlimited), Cronicle will run as many concurrent jobs as are scheduled, which can saturate the node’s CPU and memory. Cronicle runs jobs inside the application container, so size maxJobs and jobMemoryMax together under the pod memory limit. For example, four 256 MiB jobs inside a 2 GiB pod leaves headroom for the scheduler and job log buffering.

Session Secret

Parameter Type Default Description
secret.create boolean true Auto-generate a secret_key for Cronicle session encryption.
secret.existingSecret string "" Use an existing Kubernetes Secret (must contain key secret_key).
Preserve the secret_key across reinstalls

Cronicle uses secret_key to sign user sessions. If the Helm release is deleted and reinstalled with secret.create: true, a new key is generated and all existing sessions are invalidated. For stable deployments, create the Secret manually and reference it with secret.existingSecret.

Persistence

Cronicle stores all job definitions, schedules, history, and state in flat JSON files under /opt/cronicle/data. No external database is required.

Parameter Type Default Description
persistence.enabled boolean true Enable a PVC for /opt/cronicle/data.
persistence.size string 5Gi PVC size.
persistence.storageClass string "" StorageClass for the PVC.
persistence.accessModes array ["ReadWriteOnce"] PVC access modes.
persistence.existingClaim string "" Use an existing PVC instead of creating one.

Service

Parameter Type Default Description
service.type string ClusterIP Kubernetes service type.
service.port integer 80 Service port exposed to the cluster.
service.annotations object {} Annotations for the Service.

Ingress

Parameter Type Default Description
ingress.enabled boolean false Enable an Ingress resource.
ingress.ingressClassName string traefik Ingress class name.
ingress.annotations object {} Annotations for the Ingress (e.g. cert-manager).
ingress.hosts array [] Ingress host and path rules.
ingress.tls array [] TLS configuration (secret name and hosts).

Probes

Parameter Type Default Description
probes.startup.enabled boolean true Enable startup probe.
probes.startup.initialDelaySeconds integer 5 Startup probe initial delay.
probes.startup.periodSeconds integer 5 Startup probe period.
probes.startup.timeoutSeconds integer 3 Startup probe timeout.
probes.startup.failureThreshold integer 30 Startup probe failure threshold.
probes.liveness.enabled boolean true Enable liveness probe.
probes.liveness.initialDelaySeconds integer 0 Liveness probe initial delay.
probes.liveness.periodSeconds integer 15 Liveness probe period.
probes.liveness.timeoutSeconds integer 5 Liveness probe timeout.
probes.liveness.failureThreshold integer 3 Liveness probe failure threshold.
probes.readiness.enabled boolean true Enable readiness probe.
probes.readiness.initialDelaySeconds integer 0 Readiness probe initial delay.
probes.readiness.periodSeconds integer 10 Readiness probe period.
probes.readiness.timeoutSeconds integer 5 Readiness probe timeout.
probes.readiness.failureThreshold integer 3 Readiness probe failure threshold.

Resources and Security

Parameter Type Default Description
resources object {} CPU and memory requests and limits.
podSecurityContext object {} Pod-level security context.
securityContext object {} Container-level security context.

Service Account

Parameter Type Default Description
serviceAccount.create boolean false Create a dedicated ServiceAccount.
serviceAccount.name string "" Override the ServiceAccount name.
serviceAccount.annotations object {} Annotations for the ServiceAccount.

Scheduling

Parameter Type Default Description
nodeSelector object {} Node selector for scheduling.
tolerations array [] Tolerations for scheduling.
affinity object {} Affinity rules.
topologySpreadConstraints array [] Topology spread constraints.
priorityClassName string "" PriorityClass for the pod.
terminationGracePeriodSeconds integer 30 Termination grace period.
podLabels object {} Extra labels for the pod.
podAnnotations object {} Extra annotations for the pod.

Extra

Parameter Type Default Description
extraVolumes array [] Extra volumes to attach to the pod.
extraVolumeMounts array [] Extra volume mounts for the container.
extraManifests array [] Extra Kubernetes manifests deployed alongside the chart.

Common Issues

Jobs not running after pod restart

If scheduled jobs stop running after a pod restart, verify that persistence.enabled: true and that the PVC is correctly mounted. Cronicle stores all job definitions in the PVC — if it is not mounted, the scheduler starts with no configured jobs.

Viewing job output and history

Cronicle stores job run logs in the web UI under Activity Log. For debugging, live output is visible in real time during job execution. Historical log files are also stored in the PVC under /opt/cronicle/data/logs/.

More Information