9.5 KiB
Executable File
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 bykubernetes.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/v1alpha2OnionService 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 request1.5, HTTP 8080 and RTMP 1935. - LoadBalancer service with
externalTrafficPolicy: Local. - NGINX ingress for
owncast.eom.dev, with ClusterIssuerca-issuer. - OnionService
httpin namespaceowncast, 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
- Back up the data volume securely and record the live image, resource names, StorageClass, selector, and any provider annotations before deploying.
- 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. - 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.
- 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_classblank to preserve the existing claim's class. - 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.