{"id":43246925,"url":"https://github.com/gjorgji-ts/lightsout","last_synced_at":"2026-05-04T08:03:35.251Z","repository":{"id":335909250,"uuid":"1147295295","full_name":"gjorgji-ts/lightsout","owner":"gjorgji-ts","description":"LightsOut is a Kubernetes operator that automatically downscales workloads during off-hours to reduce cost and resource usage.","archived":false,"fork":false,"pushed_at":"2026-04-18T22:10:30.000Z","size":337,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-19T00:18:28.337Z","etag":null,"topics":["cost-saving","finops","k8s-operator","workload-optimization"],"latest_commit_sha":null,"homepage":"https://techsupport.mk/lightsout","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gjorgji-ts.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-01T14:25:50.000Z","updated_at":"2026-04-18T22:07:03.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/gjorgji-ts/lightsout","commit_stats":null,"previous_names":["gjorgji-ts/lightsout"],"tags_count":29,"template":false,"template_full_name":null,"purl":"pkg:github/gjorgji-ts/lightsout","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gjorgji-ts%2Flightsout","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gjorgji-ts%2Flightsout/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gjorgji-ts%2Flightsout/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gjorgji-ts%2Flightsout/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gjorgji-ts","download_url":"https://codeload.github.com/gjorgji-ts/lightsout/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gjorgji-ts%2Flightsout/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31989341,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T20:23:30.271Z","status":"ssl_error","status_checked_at":"2026-04-18T20:23:29.375Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cost-saving","finops","k8s-operator","workload-optimization"],"created_at":"2026-02-01T12:18:26.765Z","updated_at":"2026-04-19T00:18:53.991Z","avatar_url":"https://github.com/gjorgji-ts.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LightsOut\n\n[![Build](https://github.com/gjorgji-ts/lightsout/actions/workflows/main-release.yml/badge.svg)](https://github.com/gjorgji-ts/lightsout/actions/workflows/main-release.yml)\n[![Release](https://img.shields.io/github/v/release/gjorgji-ts/lightsout)](https://github.com/gjorgji-ts/lightsout/releases/latest)\n[![License](https://img.shields.io/github/license/gjorgji-ts/lightsout)](LICENSE)\n[![Go Report Card](https://goreportcard.com/badge/github.com/gjorgji-ts/lightsout)](https://goreportcard.com/report/github.com/gjorgji-ts/lightsout)\n\nLightsOut is a Kubernetes operator that automatically scales down workloads during off-hours and restores them during business hours. This helps platform engineering teams **save over 60% on development and staging cluster costs**.\n\nDefine schedules as custom resources, and LightsOut takes care of the rest. You can scale Deployments, StatefulSets, and CronJobs across namespaces according to your configured timetable without any application changes. Original replica counts are automatically preserved and restored.\n\n## Why LightsOut?\n\nDevelopment and staging clusters are typically idle outside business hours, including evenings, nights, and weekends. This equates to approximately 70% of the week, during which you’re paying for compute resources that remain unused.\n\nLightsOut, in conjunction with a node autoscaler like [Karpenter](https://karpenter.sh/), transforms idle time into savings.\n\n1. **LightsOut scales workloads to zero**: Deployments, StatefulSets, and CronJobs are scaled down or suspended according to your schedule.\n2. **Karpenter removes empty nodes**: With no workloads requesting resources, Karpenter (or Cluster Autoscaler) deprovisions the underlying nodes.\n3. **Cloud provider stops billing**: Since there are no nodes, there are no compute charges.\n\nWhen business hours resume, LightsOut restores workloads to their original replica counts, and your autoscaler provisions nodes to meet the increased demand.\n\n\u003e [!NOTE]\n\u003e LightsOut manages the workload layer, while your node autoscaler handles the infrastructure layer. Together, they eliminate idle compute costs.\n\n## Features\n\n- **Cron-based scheduling** with IANA timezone support\n- **Manages Deployments, StatefulSets, and CronJobs** - scales replicas to zero or suspends CronJobs\n- **HPA-aware scaling** - automatically patches `spec.minReplicas` on attached HorizontalPodAutoscalers to prevent fight-back, then restores on upscale\n- **Flexible namespace targeting** - label selectors, explicit lists, and exclusions\n- **Namespace-scoped schedules** - developers can define their own schedules per namespace, independent of global schedules\n- **Rate-limited scaling** - batch workloads to avoid resource spikes\n- **Admission webhooks** - validates schedules and detects overlaps before they're applied\n- **ArgoCD integration** - optional labeling of ArgoCD Application CRDs to prevent false alerts during downscale\n- **FluxCD integration** - optional suspension of FluxCD Kustomization and HelmRelease resources to prevent drift detection and alert noise during downscale\n- **Prometheus metrics** - observe schedule state, scaling operations, errors, and durations\n\n## Quick Start\n\nInstall with Helm (webhooks disabled for simplicity):\n\n```bash\nhelm install lightsout oci://ghcr.io/gjorgji-ts/charts/lightsout \\\n  --set webhook.enabled=false \\\n  --set certManager.enabled=false\n```\n\nCreate a schedule:\n\n```yaml\napiVersion: lightsout.techsupport.mk/v1alpha1\nkind: LightsOutSchedule\nmetadata:\n  name: dev-weekday-hours\nspec:\n  upscale: \"0 6 * * 1-5\"        # 6 AM Monday–Friday\n  downscale: \"0 18 * * 1-5\"     # 6 PM Monday–Friday\n  timezone: \"America/New_York\"\n  namespaceSelector:\n    matchLabels:\n      environment: dev\n```\n\nCheck status:\n\n```bash\nkubectl get lightsoutschedules\n```\n\n```\nNAME               STATE   UPSCALE       DOWNSCALE     SUSPENDED   AGE\ndev-weekday-hours  Up      0 6 * * 1-5   0 18 * * 1-5  false       7d\n```\n\nFor production setups with webhook validation, see the [Setup Guide](docs/setup-guide.md).\n\n## How It Works\n\nLightsOut runs as a controller that watches two custom resource types:\n\n- **`LightsOutSchedule`** (cluster-scoped) - for platform teams managing cost policies across multiple namespaces. Supports label selectors and explicit namespace lists.\n- **`LightsOutNamespaceSchedule`** (namespace-scoped) - for developers who want to define their own schedule for their namespace. When a namespace schedule exists, any global schedule targeting that namespace automatically skips it.\n\nOn each reconciliation, the controller calculates whether the current time falls in an \"up\" or \"down\" period based on your cron expressions, discovers target namespaces and workloads, and scales accordingly. Original replica counts are stored in annotations so they can be restored exactly.\n\nFor a deeper look at the architecture, see [docs/architecture.md](docs/architecture.md).\n\n## Configuration\n\n### `LightsOutSchedule` (cluster-scoped)\n\nManaged by platform teams. Targets workloads across one or more namespaces.\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| `upscale` | string | Yes | Cron expression for scaling up |\n| `downscale` | string | Yes | Cron expression for scaling down |\n| `timezone` | string | No | IANA timezone (default: `UTC`) |\n| `namespaceSelector` | LabelSelector | No | Select namespaces by label |\n| `namespaces` | []string | No | Explicit list of namespace names |\n| `excludeNamespaces` | []string | No | Namespaces to exclude |\n| `suspend` | bool | No | Pause all operations (default: `false`) |\n| `workloadTypes` | []string | No | Filter by type: `Deployment`, `StatefulSet`, `CronJob` |\n| `excludeLabels` | LabelSelector | No | Skip workloads matching these labels |\n| `upscaleRateLimit` | RateLimitConfig | No | Rate limit upscale operations |\n| `downscaleRateLimit` | RateLimitConfig | No | Rate limit downscale operations |\n| `argoCD` | ArgoCDConfig | No | Enable [ArgoCD integration](docs/argocd.md) |\n| `fluxCD` | FluxCDConfig | No | Enable [FluxCD integration](docs/fluxcd.md) |\n\nAt least one of `namespaceSelector` or `namespaces` must be specified.\n\n### `LightsOutNamespaceSchedule` (namespace-scoped)\n\nCreated by developers in their own namespace. No namespace selection fields - the schedule always manages the namespace it lives in. Supports all the same scheduling fields as `LightsOutSchedule`.\n\n```yaml\napiVersion: lightsout.techsupport.mk/v1alpha1\nkind: LightsOutNamespaceSchedule\nmetadata:\n  name: team-hours\n  namespace: team-a\nspec:\n  upscale: \"0 8 * * 1-5\"        # 8 AM Monday–Friday\n  downscale: \"0 20 * * 1-5\"     # 8 PM Monday–Friday\n  timezone: \"Europe/Berlin\"\n```\n\nWhen this resource exists in a namespace, any `LightsOutSchedule` targeting that namespace will skip it automatically.\n\n### Excluding Workloads\n\nTo protect specific workloads from scaling, use `excludeLabels` on the schedule:\n\n```yaml\nspec:\n  excludeLabels:\n    matchLabels:\n      critical: \"true\"\n```\n\nAny workloads with matching labels will be skipped during scaling operations.\n\n### Rate Limiting\n\nGradually scale workloads in batches to avoid resource spikes:\n\n```yaml\nspec:\n  downscaleRateLimit:\n    batchSize: 10\n    delayBetweenBatches: \"5s\"\n```\n\n### ArgoCD Integration\n\nIf you use ArgoCD, enabling the `argoCD` field prevents ArgoCD from firing false \"Degraded\" or \"OutOfSync\" alerts when LightsOut scales workloads down:\n\n```yaml\nspec:\n  argoCD:\n    namespace: argocd    # optional, defaults to \"argocd\"\n```\n\nLightsOut labels ArgoCD Application CRDs with `lightsout.techsupport.mk/state: down` during downscale and removes the labels on upscale. See the [ArgoCD Integration Guide](docs/argocd.md) for details.\n\n### FluxCD Integration\n\nIf you use FluxCD, enabling the `fluxCD` field suspends matching Kustomization and HelmRelease resources during downscale, preventing FluxCD from reconciling workloads back to their Git-defined state while they are intentionally scaled to zero:\n\n```yaml\nspec:\n  fluxCD:\n    namespace: flux-system    # optional, defaults to \"flux-system\"\n```\n\nLightsOut sets `spec.suspend: true` on matching Flux resources during downscale and resumes them (with a warming-up grace period) on upscale. See the [FluxCD Integration Guide](docs/fluxcd.md) for details.\n\n\u003e **Note:** You must opt in to the required RBAC by setting `rbac.fluxcd: true` in your Helm values.\n\n## Observability\n\nLightsOut exposes Prometheus metrics on the metrics endpoint:\n\n| Metric | Type | Description |\n|--------|------|-------------|\n| `lightsout_schedule_state` | Gauge | Current state per schedule (1=Up, 0=Down) |\n| `lightsout_next_transition_seconds` | Gauge | Seconds until next state transition |\n| `lightsout_scaling_operations_total` | Counter | Total scaling operations by schedule, namespace, type |\n| `lightsout_scaling_errors_total` | Counter | Total scaling errors |\n| `lightsout_managed_workloads` | Gauge | Number of workloads being managed |\n| `lightsout_scaling_batches_total` | Counter | Batches processed during scaling |\n| `lightsout_scaling_workloads_processed_total` | Counter | Workloads processed with success/failure result |\n| `lightsout_scaling_duration_seconds` | Histogram | Time taken for scaling operations |\n| `lightsout_last_reconcile_timestamp_seconds` | Gauge | Unix timestamp of last reconciliation |\n\nScaling events are also recorded as Kubernetes Events on the `LightsOutSchedule` and `LightsOutNamespaceSchedule` resources.\n\n## Using with Karpenter\n\nLightsOut is designed to work seamlessly with [Karpenter](https://karpenter.sh/) to achieve maximum cost savings. There’s no need for any special configuration, the two systems automatically complement each other.\n\n- **LightsOut** monitors cron schedules and scales workloads to zero during off-hours.\n- **Karpenter** monitors node utilization and removes nodes that are no longer needed.\n\nAs long as Karpenter’s `NodePool` consolidation policy is enabled (the default setting), empty nodes are drained and terminated within minutes of LightsOut scaling workloads down.\n\nThis approach also works with [Cluster Autoscaler](https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler). Any node autoscaler that deprovisions underutilized nodes will produce the same effect.\n\n## Documentation\n\n- [Architecture](docs/architecture.md) - how LightsOut works internally\n- [HPA Integration](docs/hpa.md) - automatic HorizontalPodAutoscaler handling\n- [ArgoCD Integration](docs/argocd.md) - prevent false alerts when scaling down\n- [FluxCD Integration](docs/fluxcd.md) - prevent drift detection when scaling down\n- [Setup Guide](docs/setup-guide.md) - installation with and without webhooks\n- [Security Model](docs/security-model.md) - RBAC, risks, and mitigations\n- [Examples](examples/) - sample schedule configurations\n\n## License\n\nApache License 2.0 - see [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgjorgji-ts%2Flightsout","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgjorgji-ts%2Flightsout","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgjorgji-ts%2Flightsout/lists"}