167 lines
9.5 KiB
Markdown
Executable File
167 lines
9.5 KiB
Markdown
Executable File
# ansible-role-owncast
|
||
|
||
Reusable Ansible role for deploying [Owncast](https://owncast.online/) 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](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](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](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](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](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](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](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](LICENSE). |