diff --git a/flood/Chart.yaml b/flood/Chart.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..3cc331568f73f13d4c12afbf7c409cfdbf8966ac
--- /dev/null
+++ b/flood/Chart.yaml
@@ -0,0 +1,6 @@
+apiVersion: v2
+name: flood
+description: Helm Chart for flood
+type: application
+version: 0.1.0
+appVersion: "4.7.0"
diff --git a/flood/pipeline.yml b/flood/pipeline.yml
new file mode 100644
index 0000000000000000000000000000000000000000..09aa868de68ebbf7f590f3c87fd193c79879d850
--- /dev/null
+++ b/flood/pipeline.yml
@@ -0,0 +1,16 @@
+lint-flood:
+  stage: lint
+  script:
+    - helm lint flood
+
+release-flood:
+  stage: release
+  needs:
+    - lint-flood
+  rules:
+    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
+  script:
+    - apk add --no-cache git
+    - helm plugin install https://github.com/chartmuseum/helm-push.git
+    - helm repo add --username gitlab-ci-token --password $CI_JOB_TOKEN repo ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/helm/stable
+    - helm cm-push flood repo
diff --git a/flood/templates/_helpers.tpl b/flood/templates/_helpers.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..737dba20ccbb672dd0159a25d6553cc382b03bbc
--- /dev/null
+++ b/flood/templates/_helpers.tpl
@@ -0,0 +1,56 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "flood-helm.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "flood-helm.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "flood-helm.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "flood-helm.labels" -}}
+helm.sh/chart: {{ include "flood-helm.chart" . }}
+{{ include "flood-helm.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "flood-helm.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "flood-helm.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+
+{{- define "flood-helm.sslPath" -}}
+/certs
+{{- end }}
diff --git a/flood/templates/deployment.yaml b/flood/templates/deployment.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..b56d3f93becb857eb81941cfb5f9c453949e5ed8
--- /dev/null
+++ b/flood/templates/deployment.yaml
@@ -0,0 +1,72 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ include "flood-helm.fullname" . }}
+  labels:
+    {{- include "flood-helm.labels" . | nindent 4 }}
+spec:
+  replicas: {{ .Values.replicaCount }}
+  selector:
+    matchLabels:
+      {{- include "flood-helm.selectorLabels" . | nindent 6 }}
+  template:
+    metadata:
+      {{- with .Values.podAnnotations }}
+      annotations:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      labels:
+        {{- include "flood-helm.selectorLabels" . | nindent 8 }}
+    spec:
+      {{- with .Values.imagePullSecrets }}
+      imagePullSecrets:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      securityContext:
+        {{- toYaml .Values.podSecurityContext | nindent 8 }}
+      volumes:
+        - name: data
+          {{- .Values.volume | nindent 10 }}
+      containers:
+        - name: flood
+          securityContext:
+            {{- toYaml .Values.securityContext | nindent 12 }}
+          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+          imagePullPolicy: {{ .Values.image.pullPolicy }}
+          args:
+            - "--rthost={{ .Values.rtorrent.host }}"
+            - "--rtport={{ .Values.rtorrent.port }}"
+            - "--auth={{ .Values.auth }}"
+          ports:
+            - name: http
+              containerPort: 3000
+              protocol: TCP
+          startupProbe:
+            httpGet:
+              path: /
+              port: http
+          livenessProbe:
+            httpGet:
+              path: /
+              port: http
+          readinessProbe:
+            httpGet:
+              path: /
+              port: http
+          resources:
+            {{- toYaml .Values.resources | nindent 12 }}
+          volumeMounts:
+            - mountPath: "/data"
+              name: data
+      {{- with .Values.nodeSelector }}
+      nodeSelector:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.affinity }}
+      affinity:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.tolerations }}
+      tolerations:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
diff --git a/flood/templates/ingress.yaml b/flood/templates/ingress.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..22ca81e981a46a2726ab747e01cb1085a7d24e3f
--- /dev/null
+++ b/flood/templates/ingress.yaml
@@ -0,0 +1,20 @@
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  name: {{ include "flood-helm.fullname" . }}
+  labels:
+    {{- include "flood-helm.labels" . | nindent 4 }}
+  annotations:
+    {{- .Values.ingress.annotations | toYaml | nindent 4 }}
+spec:
+  rules:
+    - host: "{{ .Values.ingress.host }}"
+      http:
+        paths:
+          - path: "{{ .Values.ingress.path }}"
+            backend:
+              service:
+                name: {{ include "flood-helm.fullname" . }}
+                port:
+                  name: http
+            pathType: Prefix
diff --git a/flood/templates/service.yaml b/flood/templates/service.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ef11a2e8171514fff22f6e5a7ce4842e130fd61a
--- /dev/null
+++ b/flood/templates/service.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ include "flood-helm.fullname" . }}
+  labels:
+    {{- include "flood-helm.labels" . | nindent 4 }}
+spec:
+  type: {{ .Values.service.type }}
+  ports:
+    - port: 80
+      targetPort: http
+      protocol: TCP
+      name: http
+  selector:
+    {{- include "flood-helm.selectorLabels" . | nindent 4 }}
diff --git a/flood/values.yaml b/flood/values.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..374d3ee16dc42d5ceddb1be4e6803742af100a68
--- /dev/null
+++ b/flood/values.yaml
@@ -0,0 +1,52 @@
+replicaCount: 1
+
+image:
+  repository: jesec/flood
+  pullPolicy: IfNotPresent
+  tag: ""
+
+imagePullSecrets: [ ]
+nameOverride: ""
+fullnameOverride: ""
+
+rtorrent:
+  host: "example.tld"
+  port: 5000
+
+auth: "default"
+
+volume: |-
+  emptyDir: {}
+
+service:
+  type: ClusterIP
+
+ingress:
+  host: "example.com"
+  path: "/"
+  annotations: { }
+
+securityContext:
+  capabilities:
+    drop:
+      - ALL
+  readOnlyRootFilesystem: true
+
+resources:
+  limits:
+    cpu: 500m
+    memory: 2Gi
+  requests:
+    cpu: 200m
+    memory: 500Mi
+
+podAnnotations: { }
+
+podSecurityContext:
+  fsGroup: 2000
+
+nodeSelector: { }
+
+tolerations: [ ]
+
+affinity: { }
diff --git a/rtorrent/Chart.yaml b/rtorrent/Chart.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..36279d3c36be8c89cb61c6aebfe7d7e4f515e503
--- /dev/null
+++ b/rtorrent/Chart.yaml
@@ -0,0 +1,6 @@
+apiVersion: v2
+name: rtorrent
+description: Helm Chart for rtorrent
+type: application
+version: 0.1.0
+appVersion: "4b75e358"
diff --git a/rtorrent/pipeline.yml b/rtorrent/pipeline.yml
new file mode 100644
index 0000000000000000000000000000000000000000..76a019a7b68536d255c12ae7c7e652774a5ff66b
--- /dev/null
+++ b/rtorrent/pipeline.yml
@@ -0,0 +1,16 @@
+lint-rtorrent:
+  stage: lint
+  script:
+    - helm lint rtorrent
+
+release-rtorrent:
+  stage: release
+  needs:
+    - lint-rtorrent
+  rules:
+    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
+  script:
+    - apk add --no-cache git
+    - helm plugin install https://github.com/chartmuseum/helm-push.git
+    - helm repo add --username gitlab-ci-token --password $CI_JOB_TOKEN repo ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/helm/stable
+    - helm cm-push rtorrent repo
diff --git a/rtorrent/templates/_helpers.tpl b/rtorrent/templates/_helpers.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..b25b5bc8f108d15dc0fc5af316b957e0e1eddd00
--- /dev/null
+++ b/rtorrent/templates/_helpers.tpl
@@ -0,0 +1,56 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "rtorrent-helm.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "rtorrent-helm.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "rtorrent-helm.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "rtorrent-helm.labels" -}}
+helm.sh/chart: {{ include "rtorrent-helm.chart" . }}
+{{ include "rtorrent-helm.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "rtorrent-helm.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "rtorrent-helm.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+
+{{- define "rtorrent-helm.sslPath" -}}
+/certs
+{{- end }}
diff --git a/rtorrent/templates/configmap.yaml b/rtorrent/templates/configmap.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..f132d2c32966965321caebf3e47671716ce97146
--- /dev/null
+++ b/rtorrent/templates/configmap.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: {{ include "rtorrent-helm.fullname" . }}
+  labels:
+    {{- include "rtorrent-helm.labels" . | nindent 4 }}
+data:
+  {{ .Values.rtorrent.config | toYaml | nindent 2 }}
diff --git a/rtorrent/templates/deployment.yaml b/rtorrent/templates/deployment.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..b4d38f6708743ee2cbf684539d9fbe53fb81788f
--- /dev/null
+++ b/rtorrent/templates/deployment.yaml
@@ -0,0 +1,107 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ include "rtorrent-helm.fullname" . }}
+  labels:
+    {{- include "rtorrent-helm.labels" . | nindent 4 }}
+spec:
+  replicas: {{ .Values.replicaCount }}
+  selector:
+    matchLabels:
+      {{- include "rtorrent-helm.selectorLabels" . | nindent 6 }}
+  template:
+    metadata:
+      {{- with .Values.podAnnotations }}
+      annotations:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      labels:
+        {{- include "rtorrent-helm.selectorLabels" . | nindent 8 }}
+    spec:
+      {{- with .Values.imagePullSecrets }}
+      imagePullSecrets:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      securityContext:
+        {{- toYaml .Values.podSecurityContext | nindent 8 }}
+      volumes:
+        - name: tmp
+          emptyDir: {}
+        - name: data
+          {{- .Values.volumes.data | nindent 10 }}
+        - name: session
+          {{- .Values.volumes.session | nindent 10 }}
+      {{ if .Values.wireguard.enabled }}
+        - name: wireguard
+          secret:
+            secretName: {{ include "rtorrent-helm.fullname" . }}
+            defaultMode: 0640
+      initContainers:
+        - name: wireguard
+          securityContext:
+            {{- toYaml .Values.wireguard.securityContext | nindent 12 }}
+          image: "{{ .Values.image.repository }}:wireguard-{{ .Values.image.tag | default .Chart.AppVersion }}"
+          imagePullPolicy: {{ .Values.image.pullPolicy }}
+          {{ if ne .Values.wireguard.localNetworks "" }}
+          env:
+            - name: LOCAL_NETWORKS
+              value: {{ .Values.wireguard.localNetworks }}
+          {{ end }}
+          resources:
+            {{- toYaml .Values.wireguard.resources | nindent 12 }}
+          volumeMounts:
+            - mountPath: "/wireguard.conf"
+              name: wireguard
+              subPath: "wireguard.conf"
+      {{ end }}
+      containers:
+        - name: rtorrent
+          securityContext:
+            {{- toYaml .Values.rtorrent.securityContext | nindent 12 }}
+          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+          imagePullPolicy: {{ .Values.image.pullPolicy }}
+          {{ if ne .Values.rtorrent.externalIpCommand "" }}
+          env:
+            - name: EXTERNAL_IP_CMD
+              value: "{{ .Values.rtorrent.externalIpCommand }}"
+          {{ else if ne .Values.rtorrent.externalIp "" }}
+            - name: EXTERNAL_IP
+              value: "{{ .Values.rtorrent.externalIp }}"
+          {{ end }}
+          envFrom:
+            - configMapRef:
+                name: {{ include "rtorrent-helm.fullname" . }}
+          ports:
+            - name: xmlrpc
+              containerPort: 5000
+              protocol: TCP
+          startupProbe:
+            tcpSocket:
+              port: xmlrpc
+          livenessProbe:
+            tcpSocket:
+              port: xmlrpc
+          readinessProbe:
+            tcpSocket:
+              port: xmlrpc
+          resources:
+            {{- toYaml .Values.rtorrent.resources | nindent 12 }}
+          volumeMounts:
+            - mountPath: "/data"
+              name: data
+            - mountPath: "/session"
+              name: session
+            - mountPath: "/tmp"
+              name: tmp
+      {{- with .Values.nodeSelector }}
+      nodeSelector:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.affinity }}
+      affinity:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.tolerations }}
+      tolerations:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
diff --git a/rtorrent/templates/secret.yaml b/rtorrent/templates/secret.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a053e9973e02ebf02509d97c6a3eb7ff96ba34e6
--- /dev/null
+++ b/rtorrent/templates/secret.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: Secret
+metadata:
+  name: {{ include "rtorrent-helm.fullname" . }}
+  labels:
+    {{- include "rtorrent-helm.labels" . | nindent 4 }}
+stringData:
+  "wireguard.conf": "{{ .Values.wireguard.config }}"
diff --git a/rtorrent/templates/service.yaml b/rtorrent/templates/service.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..55b5419786498f0b1a511785ecbdcb0b66ae08fd
--- /dev/null
+++ b/rtorrent/templates/service.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ include "rtorrent-helm.fullname" . }}
+  labels:
+    {{- include "rtorrent-helm.labels" . | nindent 4 }}
+spec:
+  type: {{ .Values.service.type }}
+  ports:
+    - port: 5000
+      targetPort: xmlrpc
+      protocol: TCP
+      name: xmlrpc
+  selector:
+    {{- include "rtorrent-helm.selectorLabels" . | nindent 4 }}
diff --git a/rtorrent/values.yaml b/rtorrent/values.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..5abd0b0e4ebd0290594df17777773035ac154f3e
--- /dev/null
+++ b/rtorrent/values.yaml
@@ -0,0 +1,82 @@
+replicaCount: 1
+
+image:
+  repository: k8r.eu/justjanne/rtorrent-docker
+  pullPolicy: IfNotPresent
+  tag: ""
+
+imagePullSecrets: [ ]
+nameOverride: ""
+fullnameOverride: ""
+
+wireguard:
+  enabled: false
+  localNetworks: ""
+  config: |-
+    [Interface]
+    PrivateKey = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+    Address = XXX.XXX.XXX.XXX/32,XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/128
+    DNS = XXX.XXX.XXX.XXX
+
+    [Peer]
+    PublicKey = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+    AllowedIPs = XXX.XXX.XXX.XXX/32,XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/128
+    Endpoint = XXX.XXX.XXX.XXX:XXXXX
+  securityContext:
+    capabilities:
+      add:
+        - NET_ADMIN
+  resources:
+    limits:
+      cpu: 200m
+      memory: 100Mi
+    requests:
+      cpu: 50m
+      memory: 10Mi
+
+rtorrent:
+  externalIpCommand: ""
+  externalIp: ""
+  config:
+    RT_TRACKER_UDP: "yes"
+    RT_MAX_UP: "100"
+    RT_MAX_UP_GLOBAL: "250"
+    RT_MIN_PEERS: "20"
+    RT_MAX_PEERS: "60"
+    RT_MIN_PEERS_SEED: "30"
+    RT_MAX_PEERS_SEED: "80"
+    RT_TRACKERS_WANT: "80"
+    RT_MEMORY_MAX: "1800M"
+    RT_LOGLEVEL: "info"
+  securityContext:
+    capabilities:
+      drop:
+        - ALL
+    readOnlyRootFilesystem: true
+  resources:
+    limits:
+      cpu: 500m
+      memory: 2Gi
+    requests:
+      cpu: 200m
+      memory: 500Mi
+
+volumes:
+  data: |-
+    emptyDir: {}
+  session: |-
+    emptyDir: {}
+
+service:
+  type: ClusterIP
+
+podAnnotations: { }
+
+podSecurityContext:
+  fsGroup: 2000
+
+nodeSelector: { }
+
+tolerations: [ ]
+
+affinity: { }