2026-10-24 16:21:16 -04:00
2026-10-24 16:21:16 -04:00
2026-10-24 16:21:16 -04:00
2026-10-24 16:21:16 -04:00
2026-10-24 16:21:16 -04:00
2026-10-24 16:21:16 -04:00
2026-10-24 16:21:16 -04:00
2026-10-24 16:21:16 -04:00
2026-10-24 16:21:16 -04:00
2026-10-24 16:21:16 -04:00
2026-10-24 16:21:16 -04:00
2026-10-24 16:21:16 -04:00
2026-10-24 16:21:16 -04:00
2026-10-24 16:21:16 -04:00

ansible-role-owncast

Reusable Ansible role for deploying Owncast on an existing Kubernetes cluster. Extracted from ansible-role-eom; no EOM services, domains, certificate issuers, or Tor installation are assumed by role defaults.

Requirements

  • Ansible Core 2.16–2.19 on a Linux/macOS controller, WSL, or a Linux container.
  • Install requirements.txt with pip and the collection with ansible-galaxy collection install -r requirements.yml. Dependencies belong in the interpreter Ansible uses on localhost.
  • An authorized kubeconfig or the usual K8S_AUTH_* authentication environment settings supported by kubernetes.core.k8s.
  • A default StorageClass supporting ReadWriteOnce, an explicit StorageClass, or an existing PVC in the instance namespace. Use encrypted backing storage and arrange encrypted backups separately.
  • For optional ingress: DNS, an ingress controller, and a valid TLS secret or cert-manager with a configured ClusterIssuer. For LoadBalancer services: a working cluster load balancer implementation.
  • Optional onion access requires the Tor operator and its tor.k8s.torproject.org/v1alpha2 OnionService CRD already installed.

Check out the repository as ansible-role-owncast under a directory in ANSIBLE_ROLES_PATH. For sibling role repositories, set that environment variable to their parent directory. The supplied playbooks run on the controller using its current Kubernetes context, with no SSH target required. Deployment commands below run from this repository on the controller.

Your existing eom.dev instance

Use playbooks/deploy-eom.yml. It explicitly preserves:

  • Namespace, Deployment, Service, PVC, and TLS secret named owncast.
  • Image owncast/owncast:0.3.0, one replica, 128Gi ReadWriteOnce storage at /app/data, CPU request 1.5, HTTP 8080 and RTMP 1935.
  • LoadBalancer service with externalTrafficPolicy: Local.
  • NGINX ingress for owncast.eom.dev, with ClusterIssuer ca-issuer.
  • OnionService http in namespace owncast, port 80 → HTTP 8080, formerly part of the shared EOM onion task.

First run ansible-playbook -i localhost, playbooks/deploy-eom.yml --check --diff, review the changes, then run ansible-playbook -i localhost, playbooks/deploy-eom.yml. If Tor was never installed, add -e owncast_onion_enabled=false to both commands.

Migration safety

  1. Back up the data volume securely and record the live image, resource names, StorageClass, selector, and any provider annotations before deploying.
  2. Check the Kubernetes context. Keep the same namespace, PVC, and selector app: owncast. A different live immutable Deployment selector needs manual reconciliation; the role does not force-replace resources.
  3. Stop invoking the retired EOM Owncast task. Its deployment code, full-deployment entry, and Owncast-specific onion task have been removed. The old task path only fails with migration guidance; it does not delegate or deploy anything. No automatic dependency is added; use this playbook.
  4. Do not delete the existing PVC or TLS secret. Resources are adopted by name; administrator settings, stream keys, and the data directory are not reset. Leave owncast_storage_class blank to preserve the existing claim's class.
  5. Verify the rollout, HTTPS, administrator login, RTMP ingest, and optional onion URL after deployment.

The Deployment API is corrected from the old invalid v1 to apps/v1. Updates use Recreate rather than overlapping replicas so the SQLite database and RWO volume have a single writer; an explicit null removes inherited rollingUpdate settings when patching the existing Deployment. Expect brief downtime during an update. Removing Ansible tasks does not delete any live cluster resources.

Your friend's instance

Use playbooks/deploy-friend.yml. It uses separate namespace, resource names, PVC, and TLS secret owncast-friend, with the same image, storage sizing, resource request, and network layout as your instance. Tor is disabled. Two required inputs deliberately have no invented defaults:

  • friend_owncast_hostname: your friend's real streaming hostname.
  • friend_owncast_cluster_issuer: a ClusterIssuer valid for that hostname.

Define these in inventory variables for localhost or an external YAML vars file. Run ansible-playbook -i localhost, playbooks/deploy-friend.yml -e @/secure/friend-vars.yml. Use --check --diff first. Add --ask-vault-pass for a Vault-encrypted vars file; do not commit secrets or pass passwords on the command line. Extra variables can also override the owncast_* settings when different sizing is needed.

The playbook rejects missing inputs, the eom.dev hostname, and the existing namespace before changing the cluster. For deployment on another cluster, use a separate kubeconfig/context. On a shared cluster, plan for another load balancer address and volume. Namespace separation is not a tenant security boundary by itself.

Generic usage and variables

Invoke ansible-role-owncast from your own playbook or inventory; neither site-specific playbook is required. All defaults are in defaults/main.yml.

Variable Default / meaning
owncast_namespace, owncast_name owncast; change for independent instances
owncast_image owncast/owncast:0.3.0; override with a tested tag or digest
owncast_image_pull_policy, owncast_image_pull_secrets IfNotPresent, []; Secret references for private registries
owncast_resources Requests of 250m CPU and 256Mi memory; configure requests/limits for your transcoding workload
owncast_node_selector, owncast_tolerations {}, []
owncast_pvc_name Resource name
owncast_existing_claim Empty; nonempty skips PVC creation and mounts that existing claim in this namespace
owncast_storage_size, owncast_storage_class 10Gi, empty (default StorageClass)
owncast_service_type ClusterIP; also supports NodePort and LoadBalancer
owncast_service_annotations {}; provider-specific configuration
owncast_external_traffic_policy Local; emitted only for NodePort/LoadBalancer
owncast_load_balancer_source_ranges []; optional provider-supported CIDR restrictions on both service ports
owncast_ingress_enabled, owncast_hostname false, empty; public HTTP exposure is opt-in
owncast_ingress_class, owncast_ingress_annotations Empty (controller default), {}
owncast_tls_enabled, owncast_tls_secret_name true, resource name; applies when ingress is enabled
owncast_cluster_issuer Empty; provide a name to use cert-manager, otherwise provision the TLS secret yourself
owncast_onion_enabled, owncast_onion_name false, http

HTTP and RTMP container/service ports remain fixed at 8080 and 1935; configure external TCP routing separately if not using a LoadBalancer/NodePort service. One Owncast process per data volume is supported; horizontal scaling is not. Storage expansion depends on the StorageClass; never shrink an existing claim or change its immutable StorageClass. Changing namespaces/names creates another instance, not a data migration.

Disabling ingress or Tor stops managing it; it does not delete a previously created resource. Remove stale resources manually when revoking public access. The role uses only state: present and provides no destructive purge task.

Security and operations

  • No secrets are embedded, generated, or rotated. For a fresh instance, first deploy privately (owncast_service_type=ClusterIP, ingress disabled) and use Owncast's supported admin workflow to set unique administrator credentials and stream key before exposing it publicly. Existing credentials remain in the persistent database and are not altered by this role.
  • The site playbooks intentionally preserve existing LoadBalancer behavior: unencrypted HTTP 8080 is exposed alongside RTMP 1935, bypassing ingress TLS on that port. Restrict access with provider/firewall rules or source ranges, or use ClusterIP plus separately configured RTMP routing. RTMP itself is unencrypted; use trusted networking/tunnels when confidentiality matters.
  • TLS terminates at ingress. Use a trusted certificate/issuer; Kubernetes API certificate verification is not disabled. Storage encryption, backups, RBAC, NetworkPolicies, ingress admin restrictions, and renewal are operator duties.
  • Pods receive no Kubernetes API token, cannot escalate privileges, drop Linux capabilities, and use runtime-default seccomp. UID and data ownership are intentionally not changed during migration.
  • The old image version is retained for migration compatibility, not presented as the latest release. Review and test image upgrades separately after a backup; a digest-pinned image can be supplied via owncast_image.

Tests (no cluster needed)

tests/test.yml renders templates with actual playbook vars, checks migration identity/settings and friend isolation, exercises optional fields, and checks invalid-input rejection. tests/run.sh also syntax-checks both playbooks and tests friend preflight failures before any cluster access.

Run sh tests/run.sh on a configured controller, or build tests/Dockerfile with docker build -f tests/Dockerfile -t owncast-role-tests . and run docker run --rm --network none --cap-drop ALL --security-opt no-new-privileges owncast-role-tests. The image runs tests as a non-root user and requires no kubeconfig or secrets.

License

BSD-3-Clause; see LICENSE.

S
Description
Ansible role to deploy Owncast to Kubernetes
Readme BSD-3-Clause
37 KiB
Languages
Jinja 73.7%
Shell 19%
Dockerfile 7.3%