Files
homelab/media-services/jellyfin/index.ts

164 lines
3.8 KiB
TypeScript

import { Construct } from "constructs";
import { DeploymentV1 } from "@cdktf/provider-kubernetes/lib/deployment-v1";
import { ServiceV1 } from "@cdktf/provider-kubernetes/lib/service-v1";
import {
CloudflareCertificate,
InternalIngressRoute,
LonghornPvc,
} from "../../utils";
import { BaseMediaServiceOptions, getAamil3NodeSelector } from "../types";
type JellyfinServerOptions = BaseMediaServiceOptions & {
/** Name of the shared media PVC */
mediaPvcName: string;
/** Hostname for the ingress */
host: string;
};
export class JellyfinServer extends Construct {
constructor(scope: Construct, id: string, options: JellyfinServerOptions) {
super(scope, id);
const { provider, namespace, mediaPvcName, host } = options;
const name = "server";
// Config PVC with backup
const configPvc = new LonghornPvc(this, "config", {
provider,
name: "jellyfin-config",
namespace,
size: "5Gi",
backup: true,
});
// Service
new ServiceV1(this, "service", {
provider,
metadata: {
name,
namespace,
},
spec: {
selector: {
app: name,
},
port: [
{
name: "http",
port: 80,
targetPort: "http",
},
{
name: "discovery",
port: 7359,
targetPort: "discovery",
},
],
type: "LoadBalancer",
},
});
// Deployment
new DeploymentV1(this, "deployment", {
provider,
metadata: {
name,
namespace,
},
spec: {
replicas: "1",
selector: {
matchLabels: {
app: name,
},
},
template: {
metadata: {
labels: {
app: name,
},
},
spec: {
nodeSelector: getAamil3NodeSelector(),
container: [
{
name,
image: "jellyfin/jellyfin:latest",
imagePullPolicy: "IfNotPresent",
port: [
{
containerPort: 8096,
name: "http",
},
{
containerPort: 7359,
name: "discovery",
},
],
env: [
{
name: "TZ",
value: "Asia/Karachi",
},
],
volumeMount: [
{
name: "config",
mountPath: "/config",
},
{
name: "cache",
mountPath: "/cache",
},
{
name: "media",
mountPath: "/media",
},
],
},
],
volume: [
{
name: "config",
persistentVolumeClaim: {
claimName: configPvc.name,
},
},
{
name: "cache",
emptyDir: {},
},
{
name: "media",
persistentVolumeClaim: {
claimName: mediaPvcName,
},
},
],
},
},
},
});
new CloudflareCertificate(this, "certificate", {
provider,
namespace,
name,
secretName: "jellyfin-tls",
dnsNames: [host],
});
// Ingress - using internal ingress for secure access
new InternalIngressRoute(this, "ingress", {
provider,
namespace,
name,
host,
serviceName: name,
servicePort: 80,
tlsSecretName: "jellyfin-tls",
});
}
}