sleepyservice

module
v0.2.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 15, 2026 License: Apache-2.0

README

K8S SleepyService

Tests codecov Go Report Card Go Version License Release Artifact Hub

A Kubernetes operator that automatically hibernates (scales to zero) workloads when they're not in use and wakes them up on-demand when traffic arrives. Save resources and reduce costs for development, staging, and low-traffic environments.

Overview

SleepyService creates a smart proxy in front of your Kubernetes workloads that:

  • Hibernates your deployments, statefulsets, and databases when idle
  • Wakes them up automatically when traffic arrives
    • For interactive traffic, defined as incoming HTTP requests with text/html in Accept-Content, a friendly waiting page with real-time progress updates while the underlying infrastructure wakes up
    • For API traffic, the incoming request is delayed until the backend is ready
  • Supports automatic idle timeout for hands-free cost savings
  • Works with CloudNativePG databases for full-stack hibernation

Perfect for:

  • Development and staging environments
  • Preview environments for pull requests
  • Demo applications with sporadic usage
  • Cost optimization without sacrificing availability

How It Works

┌─────────┐      ┌──────────────┐      ┌─────────────┐
│  User   │─────▶│  Wake Proxy  │─────▶│   Backend   │
└─────────┘      └──────────────┘      │ (Your App)  │
                        │              └─────────────┘
                        │                     │
                        │              ┌─────────────┐
                        │              │  Database   │
                        │              │   (CNPG)    │
                        │              └─────────────┘
                        ▼
                 ┌──────────────┐
                 │ SleepyService│
                 │  Controller  │
                 └──────────────┘
  1. When Sleeping: All components (deployments, databases) are scaled to zero
  2. On Request: Wake proxy detects traffic and triggers the wake-up sequence
  3. During Wake: Users see a waiting page with real-time progress via SSE
  4. When Awake: Traffic is proxied transparently to your application
  5. Auto-Sleep: After configured idle time, components hibernate automatically

Note: SleepyService trusts Kubernetes readiness probes. Ensure your pods have appropriate readiness probes configured. The proxy will forward traffic as soon as Kubernetes reports pods as ready.

Quick Start

Prerequisites
  • Kubernetes cluster v1.11.3+
  • kubectl v1.11.3+
  • Go 1.25.5+ (for development)
  • Docker 17.03+ (for building images)
Installation

Option 1: Install with Helm (Recommended)

Install from the OCI registry:

helm install sleepyservice oci://ghcr.io/athalabs/charts/sleepyservice

Or install a specific version:

helm install sleepyservice oci://ghcr.io/athalabs/charts/sleepyservice --version 0.1.0

Customize the installation with values:

helm install sleepyservice oci://ghcr.io/athalabs/charts/sleepyservice \
  --set controllerManager.replicas=2 \
  --set controllerManager.container.resources.limits.cpu=1000m

Option 2: Install from GitHub Releases

Download and apply the pre-built installation bundle from the latest release:

kubectl apply -f https://github.com/athalabs/sleepyservice/releases/latest/download/install.yaml

Or install a specific version:

kubectl apply -f https://github.com/athalabs/sleepyservice/releases/download/vMAJOR.MINOR.PATCH/install.yaml

Option 3: Install from Source

  1. Install the CRDs:
make install
  1. Deploy the operator:
make deploy IMG=<your-registry>/sleepyservice:tag

Option 4: Build and Install Locally

  1. Build the installation bundle:
make build-installer IMG=<your-registry>/sleepyservice:tag
  1. Apply the generated bundle:
kubectl apply -f dist/install.yaml
Basic Example

Create a SleepyService for a simple web application:

apiVersion: sleepy.atha.gr/v1alpha1
kind: SleepyService
metadata:
  name: my-app
  namespace: default
spec:
  # Auto-hibernate after 15 minutes of no traffic
  idleTimeout: 15m

  # Wait up to 5 minutes for wake-up
  wakeTimeout: 5m

  # Backend service configuration
  backendService:
    enabled: true
    type: ClusterIP
    ports:
      - name: http
        port: 80
        targetPort: 8080

  # Components to manage (in dependency order)
  components:
    - name: postgres
      type: CNPGCluster
      ref:
        name: my-postgres-cluster

    - name: app
      type: Deployment
      ref:
        name: my-app-deployment
      replicas: 2
      dependsOn:
        - postgres

This creates:

  • A wake proxy service at my-app.default.svc.cluster.local
  • A backend service at my-app-actual.default.svc.cluster.local
  • Automatic orchestration of PostgreSQL and app wake-up sequence

Access your app through the proxy service, and it will automatically wake up when traffic arrives!

API Reference

SleepyServiceSpec
Field Type Required Default Description
debug bool No false Enable debug endpoints / _wake/debug/wake and / _wake/debug/sleep
components []Component Yes - Components to manage, in dependency order
backendService BackendServiceSpec No - Configuration for the managed backend Service
wakeTimeout Duration No 5m Maximum time to wait for wake-up
idleTimeout Duration No 0 (disabled) Auto-hibernate after this period of inactivity
Component
Field Type Required Default Description
name string Yes - Unique identifier for this component
type ComponentType Yes - Type: Deployment, StatefulSet, or CNPGCluster
ref ResourceRef Yes - Reference to the Kubernetes resource
replicas int32 No 1 Target replicas when awake (for Deployment/StatefulSet)
dependsOn []string No - Names of components that must be ready first
BackendServiceSpec
Field Type Required Default Description
enabled bool No true Whether to create the backend Service
type ServiceType No ClusterIP Service type: ClusterIP, NodePort, LoadBalancer
ports []ServicePort No Auto-detect Ports to expose (auto-created from first container port if empty)
annotations map[string]string No - Annotations to add to the Service
clusterIP string No Auto-assign Specific ClusterIP to use
externalIPs []string No - External IPs for the Service
loadBalancerIP string No - LoadBalancer IP (for LoadBalancer type)
sessionAffinity ServiceAffinity No - Session affinity: ClientIP or None
ServicePort
Field Type Required Default Description
name string No - Port name (required if multiple ports)
protocol Protocol No TCP Protocol: TCP, UDP, or SCTP
port int32 Yes - Port to expose on the Service
targetPort IntOrString No Same as port Target port on pods
nodePort int32 No Auto-assign NodePort (for NodePort/LoadBalancer)
ResourceRef
Field Type Required Default Description
name string Yes - Name of the resource
namespace string No Same as SleepyService Namespace of the resource
SleepyServiceStatus
Field Type Description
state ServiceState Current state: Sleeping, Waking, Awake, Hibernating, Error
desiredState ServiceState Desired state set by the proxy
lastTransition Time When the state last changed
lastActivity Time When traffic was last received (set by proxy)
components []ComponentStatus Status of each managed component
proxyDeployment string Name of the created proxy deployment
backendService string Name of the created backend service
conditions []Condition Standard Kubernetes conditions

Examples

Example 1: Simple Web Application
apiVersion: sleepy.atha.gr/v1alpha1
kind: SleepyService
metadata:
  name: demo-app
spec:
  idleTimeout: 30m

  components:
    - name: web
      type: Deployment
      ref:
        name: demo-web
      replicas: 1
Example 2: Full Stack with Database
apiVersion: sleepy.atha.gr/v1alpha1
kind: SleepyService
metadata:
  name: fullstack-app
spec:
  idleTimeout: 1h
  wakeTimeout: 10m

  backendService:
    type: ClusterIP
    ports:
      - name: http
        port: 80
        targetPort: 3000
      - name: metrics
        port: 9090
        targetPort: 9090
    annotations:
      prometheus.io/scrape: "true"
      prometheus.io/port: "9090"

  components:
    # Database wakes first
    - name: database
      type: CNPGCluster
      ref:
        name: app-db

    # API depends on database
    - name: api
      type: Deployment
      ref:
        name: api-server
      replicas: 3
      dependsOn:
        - database

    # Worker depends on database
    - name: worker
      type: Deployment
      ref:
        name: background-worker
      replicas: 2
      dependsOn:
        - database
Example 3: StatefulSet Application
apiVersion: sleepy.atha.gr/v1alpha1
kind: SleepyService
metadata:
  name: stateful-app
spec:
  idleTimeout: 2h

  components:
    - name: app
      type: StatefulSet
      ref:
        name: my-statefulset
      replicas: 3
Example 4: Multi-Namespace Setup
apiVersion: sleepy.atha.gr/v1alpha1
kind: SleepyService
metadata:
  name: cross-ns-app
  namespace: frontend
spec:
  components:
    # Database in different namespace
    - name: db
      type: CNPGCluster
      ref:
        name: shared-postgres
        namespace: databases

    # App in same namespace as SleepyService
    - name: web
      type: Deployment
      ref:
        name: web-app
      dependsOn:
        - db
Example 5: Custom Backend Service Configuration
apiVersion: sleepy.atha.gr/v1alpha1
kind: SleepyService
metadata:
  name: loadbalancer-app
spec:
  backendService:
    type: LoadBalancer
    loadBalancerIP: "203.0.113.100"
    sessionAffinity: ClientIP
    ports:
      - name: https
        port: 443
        targetPort: 8443
        protocol: TCP
    annotations:
      service.beta.kubernetes.io/aws-load-balancer-type: "nlb"

  components:
    - name: app
      type: Deployment
      ref:
        name: secure-app

Component Types

Deployment

Standard Kubernetes Deployment. Scaled to 0 when sleeping, scaled to replicas when awake.

StatefulSet

Kubernetes StatefulSet with persistent volumes. Scaled to 0 when sleeping, maintaining PVC claims.

CNPGCluster

CloudNativePG PostgreSQL cluster. Hibernated using the cnpg.io/hibernation: "on" annotation.

State Transitions

     ┌──────────┐
     │ Sleeping │◀────────────┐
     └─────┬────┘             │
           │                  │
      Traffic Detected        │
           │              Idle Timeout
           ▼                  │
      ┌─────────┐             │
      │ Waking  │             │
      └────┬────┘             │
           │                  │
   All Components Ready       │
           │                  │
           ▼                  │
       ┌───────┐              │
       │ Awake │──────────────┘
       └───────┘

Wake Proxy Features

The automatically created wake proxy provides:

  • Waiting Page: Beautiful UI with real-time progress updates via Server-Sent Events
  • Readiness Aware: Relies on Kubernetes readiness probes to determine when services are ready
  • Idle Detection: Tracks traffic and triggers auto-hibernation
Proxy Endpoints
  • /_wake/status - JSON status of current state
  • /_wake/events - SSE stream for real-time updates
  • /_wake/health - Proxy health check
  • (POST) /_wake/debug/wake - (if debug: true) trigger wake-up manually
  • (POST) /_wake/debug/sleep - (if debug: true) trigger sleep manually

All other paths are proxied to your backend application.

Development

Running Locally
# Install CRDs
make install

# Run controller locally
make run

# Run tests
make test

# Run tests with coverage summary
make test-coverage

# Generate HTML coverage report
make test-coverage-html

# Build binary
make build
Building and Deploying
# Build and push image
make docker-build docker-push IMG=<your-registry>/sleepyservice:tag

# Deploy to cluster
make deploy IMG=<your-registry>/sleepyservice:tag

# View logs
kubectl logs -n sleepyservice-system deployment/sleepyservice-controller-manager -f
Cleanup
# Delete sample instances
kubectl delete -k config/samples/

# Uninstall CRDs
make uninstall

# Remove controller
make undeploy

Architecture Details

Operator Components
  1. Controller: Reconciles SleepyService resources, manages component scaling
  2. Wake Proxy: Lightweight proxy that handles traffic interception and wake-up
  3. RBAC Setup: ServiceAccount, Role, and RoleBinding for proxy permissions
How Components Wake Up
  1. Proxy receives traffic while services are sleeping
  2. Proxy updates SleepyService.status.desiredState to Awake
  3. Controller detects state change and starts wake-up sequence
  4. Controller scales components in dependency order
  5. Controller updates SleepyService.status.state to Waking
  6. Proxy polls status and updates waiting page in real-time
  7. Controller detects all components ready via K8s readiness probes, sets state to Awake
  8. Proxy starts forwarding traffic
Resource Ownership
  • Wake proxy Deployment, Service, ServiceAccount, Role, and RoleBinding are owned by the SleepyService
  • Backend Service (if created) is owned by the SleepyService
  • Managed components (Deployments, StatefulSets, CNPG clusters) are NOT owned - only scaled

Troubleshooting

Check SleepyService Status
kubectl get sleepyservice my-app -o yaml

Look at:

  • .status.state - Current state
  • .status.components - Per-component readiness
  • .status.conditions - Detailed conditions
Check Proxy Logs
kubectl logs deployment/my-app-wakeproxy -f
Check Controller Logs
kubectl logs -n sleepyservice-system deployment/sleepyservice-controller-manager -f
Common Issues

Components not waking up

  • Verify RBAC permissions for the controller
  • Check component selectors and references are correct
  • Ensure components exist in specified namespaces

Wake proxy shows error

  • Check backend service configuration
  • Ensure target deployments have correct labels

Auto-hibernation not working

  • Confirm idleTimeout is set and non-zero
  • Check proxy is receiving and tracking traffic
  • Verify proxy has permissions to update SleepyService status

Project Distribution

YAML Bundle

The installation bundle (install.yaml) contains all required resources:

  • Custom Resource Definitions (CRDs)
  • Operator deployment and RBAC
  • Default configuration

For End Users:

Download from GitHub Releases:

kubectl apply -f https://github.com/athalabs/sleepyservice/releases/latest/download/install.yaml

For Developers/Maintainers:

Build the installer locally:

make build-installer IMG=ghcr.io/athalabs/sleepyservice:v0.1.0

This generates dist/install.yaml with all manifests bundled together. The dist/ folder is created during the build process and is not checked into version control.

Helm Chart

For End Users:

The Helm chart is published to GitHub Container Registry as an OCI artifact:

# Install latest version
helm install sleepyservice oci://ghcr.io/athalabs/charts/sleepyservice

# Install specific version
helm install sleepyservice oci://ghcr.io/athalabs/charts/sleepyservice --version 0.1.0

# Install in a specific namespace
helm install sleepyservice oci://ghcr.io/athalabs/charts/sleepyservice \
  --namespace sleepyservice-system \
  --create-namespace

# Customize values
helm install sleepyservice oci://ghcr.io/athalabs/charts/sleepyservice \
  --set controllerManager.replicas=2 \
  --set metrics.enable=true \
  --set prometheus.enable=true

# Upgrade an existing installation
helm upgrade sleepyservice oci://ghcr.io/athalabs/charts/sleepyservice --version 0.2.0

# Uninstall
helm uninstall sleepyservice

Available Configuration Options:

See dist/chart/values.yaml for all configurable values, including:

  • controllerManager.replicas - Number of controller manager replicas (default: 1)
  • controllerManager.container.image.repository - Image repository
  • controllerManager.container.image.tag - Image tag
  • controllerManager.container.resources - CPU and memory limits/requests
  • rbac.enable - Enable RBAC (default: true)
  • crd.enable - Install CRDs (default: true)
  • crd.keep - Keep CRDs on uninstall (default: true)
  • metrics.enable - Enable metrics export (default: true)
  • prometheus.enable - Enable Prometheus ServiceMonitor (default: false)

For Developers/Chart Publishers:

Generate and publish the Helm chart:

# Regenerate chart from kustomize manifests
kubebuilder edit --plugins=helm/v2-alpha

# Chart is available in dist/chart/

# Test the chart locally
helm lint dist/chart
helm template test dist/chart

# Package the chart
helm package dist/chart

# Push to OCI registry (requires authentication)
helm push sleepyservice-0.1.0.tgz oci://ghcr.io/athalabs/charts

The Helm chart is automatically published to the OCI registry on every GitHub release via CI/CD.

Contributing

Contributions are welcome! This project uses:

  • Kubebuilder for operator scaffolding
  • Standard Go testing with make test
  • golangci-lint for code quality

Run make help for all available targets.

License

Copyright 2026 AthaLabs

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Directories

Path Synopsis
api
v1alpha1
Package v1alpha1 contains API Schema definitions for the sleepy v1alpha1 API group.
Package v1alpha1 contains API Schema definitions for the sleepy v1alpha1 API group.
cmd
manager command
proxy command
internal
test

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL