diff --git a/charts/graylog/.helmignore b/charts/graylog/.helmignore
new file mode 100644
index 0000000000000000000000000000000000000000..0e8a0eb36f4ca2c939201c0d54b5d82a1ea34778
--- /dev/null
+++ b/charts/graylog/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/charts/graylog/Chart.lock b/charts/graylog/Chart.lock
new file mode 100644
index 0000000000000000000000000000000000000000..4b665ed819fb2eec0391e532e32e49a6d8e68279
--- /dev/null
+++ b/charts/graylog/Chart.lock
@@ -0,0 +1,9 @@
+dependencies:
+- name: mongodb
+  repository: https://groundhog2k.github.io/helm-charts
+  version: 0.2.7
+- name: elasticsearch
+  repository: https://groundhog2k.github.io/helm-charts
+  version: 0.1.0
+digest: sha256:a060aad91af87a477e72361960c442acef3e4851fcc41690058d970d25481190
+generated: "2021-01-04T14:04:19.2835459+01:00"
diff --git a/charts/graylog/Chart.yaml b/charts/graylog/Chart.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ad257804daf158fe46eb8f5ff18015edd15f3f22
--- /dev/null
+++ b/charts/graylog/Chart.yaml
@@ -0,0 +1,22 @@
+apiVersion: v2
+name: graylog
+description: A Helm chart for Graylog on Kubernetes
+
+type: application
+
+maintainers:
+  - name: groundhog2k
+
+version: 0.1.0
+
+appVersion: "4.0.1"
+
+dependencies:
+  - name: mongodb
+    version: 0.2.7
+    repository: "https://groundhog2k.github.io/helm-charts"
+    condition: mongodb.enabled
+  - name: elasticsearch
+    version: 0.1.0
+    repository: "https://groundhog2k.github.io/helm-charts"
+    condition: elasticsearch.enabled
diff --git a/charts/graylog/README.md b/charts/graylog/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..a154b8f9f690ca1b7e46724fb877c33ff01945c4
--- /dev/null
+++ b/charts/graylog/README.md
@@ -0,0 +1,181 @@
+# Graylog
+
+![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 4.0.1](https://img.shields.io/badge/AppVersion-4.0.1-informational?style=flat-square)
+
+A Helm chart for Graylog on Kubernetes
+
+## TL;DR
+
+```bash
+$ helm repo add groundhog2k https://groundhog2k.github.io/helm-charts/
+$ helm install my-release groundhog2k/graylog
+```
+
+## Introduction
+
+This chart uses the original [Graylog image from Docker Hub](https://hub.docker.com/r/graylog/graylog/) to deploy a stateful Graylog instance in a Kubernetes cluster.
+
+## Prerequisites
+
+- Kubernetes 1.12+
+- Helm 3.x
+- PV provisioner support in the underlying infrastructure
+
+## Installing the Chart
+
+To install the chart with the release name `my-release`:
+
+```bash
+$ helm install my-release groundhog2k/graylog
+```
+
+## Uninstalling the Chart
+
+To uninstall/delete the `my-release` deployment:
+
+```bash
+$ helm uninstall my-release
+```
+
+## Common parameters
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| fullnameOverride | string | `""` | Fully override the deployment name |
+| nameOverride | string | `""` | Partially override the deployment name |
+
+## Deployment parameters
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy |
+| image.repository | string | `"graylog/graylog"` | Image name |
+| image.tag | string | `""` | Image tag |
+| initImage.pullPolicy | string | `"IfNotPresent"` | Init container image pull policy |
+| initImage.repository | string | `"busybox"` | Default init container image |
+| initImage.tag | string | `"latest"` | Init container image tag |
+| imagePullSecrets | list | `[]` | Image pull secrets |
+| livenessProbe | object | `see values.yaml` | Liveness probe configuration |
+| readinessProbe | object | `see values.yaml` | Readiness probe configuration |
+| resources | object | `{}` | Resource limits and requests |
+| nodeSelector | object | `{}` | Deployment node selector |
+| podAnnotations | object | `{}` | Additional pod annotations |
+| podSecurityContext | object | `see values.yaml` | Pod security context |
+| securityContext | object | `see values.yaml` | Container security context |
+| env | list | `[]` | Additional container environmment variables |
+| args | list | `[]` | Additional container command arguments |
+| serviceAccount.annotations | object | `{}` | Additional service account annotations |
+| serviceAccount.create | bool | `false` | Enable service account creation |
+| serviceAccount.name | string | `""` | Name of the service account |
+| affinity | object | `{}` | Pod affinity |
+| tolerations | list | `[]` | Pod tolerations |
+| podManagementPolicy | string | `OrderedReady` | Pod management policy |
+| updateStrategyType | string | `RollingUpdate` | Update strategy |
+| replicaCount | int | `1` | Number of replicas (Not supported - Don't change in this chart version) |
+
+## Service paramters
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| service.type | string | `"ClusterIP"` | Service type |
+| service.http | int | `80` | Graylog http service port |
+| service.nodePort | int | `nil` | The http node port (only relevant for type LoadBalancer or NodePort) |
+| service.clusterIP | string | `nil` | The cluster ip address (only relevant for type LoadBalancer or NodePort) |
+| service.loadBalancerIP | string | `nil` | The load balancer ip address (only relevant for type LoadBalancer) |
+
+## Extra service parameters
+
+Section to define all additional UDP/TCP inputs for Graylog
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| extraServices[].name | string | `nil` | Unique name of the input service |
+| extraServices[].type | string | `nil` | Service type (ClusterIP / NodePort / LoadBalancer) |
+| extraServices[].protocol | string | `nil` | Protocol type (TCP / UDP) |
+| extraServices[].containerPort | int | `nil` | Container port |
+| extraServices[].port | int | `nil` | Service port |
+| extraServices[].nodePort | int | `nil` | The http node port (only relevant for type LoadBalancer or NodePort) |
+| extraServices[].clusterIP | string | `nil` | The cluster ip address (only relevant for type LoadBalancer or NodePort) |
+| extraServices[].loadBalancerIP | string | `nil` | The load balancer ip address (only relevant for type LoadBalancer) |
+
+
+## Ingress parameters
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| ingress.enabled | bool | `false` | Enable ingress for Gitea service |
+| ingress.annotations | string | `nil` | Additional annotations for ingress |
+| ingress.hosts[].host | string | `nil` | Hostname for the ingress endpoint |
+| ingress.hosts[].host.paths[] | string | `nil` | Path routing for the ingress endpoint host |
+| ingress.tls | list | `[]` | Ingress TLS parameters |
+
+## Database settings
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| externalDatabase.host | string | `nil` | External MongoDB database host |
+| externalDatabase.name | string | `"graylog"` | External database name |
+| externalDatabase.user | string | `nil` | External database user name |
+| externalDatabase.password | string | `nil` | External database user password |
+| mongodb.enabled | bool | `false` | Enable MongoDB deployment (will disable external database settings) |
+| mongodb.settings.rootUsername | string | `admin` | The root username |
+| mongodb.settings.rootPassword | string | `{}` | The root users password (Random value if not specified) |
+| mongodb.userDatabase | object | `{}` | Optional MongoDB user database |
+| mongodb.userDatabase.name | string | `nil` | Name of the user database |
+| mongodb.userDatabase.user | string | `nil` | User name with full access to user database|
+| mongodb.userDatabase.password | string | `nil` | Password of created user (Random value if not specified) |
+| mongodb.storage | object | `see values.yaml` | MongoDB storage settings |
+| elasticsearch.enabled | bool | `false` | Enable Elasticsearch deployment (will disable `elastic.hosts` setting) |
+| elasticsearch.javaOpts | string | `"-Xms512m -Xmx512m"` | Additional JVM options for Elasticsearch |
+| elasticsearch.clusterName | string | `"graylog"` | Elasticsearch cluster name |
+| elasticsearch.storage | object | `see values.yaml` | Elasticsearch storage settings |
+
+## MaxMind GeoIP2 database
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| initGeoIPDatabase.enabled | bool | `false` | Enable GeoIP database download |
+| initGeoIPDatabase.accountId | string | `""` | MaxMind UserId / AccountId |
+| initGeoIPDatabase.licenseKey | string | `""` | MaxMind license key |
+| initGeoIPDatabase.editionId | string | `"GeoLite2-City"` | Default database edition Id (https://www.maxmind.com/en/accounts/473747/geoip/downloads) |
+| initGeoIPDatabase.host | string | `""` | The MaxMind download host (not necessary to change that - default updates.maxmind.com)|
+| initGeoIPDatabase.proxy | string | `""` | A valid proxy if internet access is running through a proxy |
+| initGeoIPDatabase.proxyUserPassword | string | `""` | Proxy username and password in format "username:password" |
+
+## Storage parameters
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| storage.accessModes[0] | string | `"ReadWriteOnce"` | Storage access mode |
+| storage.persistentVolumeClaimName | string | `nil` | PVC name when existing storage volume should be used |
+| storage.requestedSize | string | `nil` | Size for new PVC, when no existing PVC is used |
+| storage.className | string | `nil` | Storage class name |
+
+## Graylog parameters
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| settings.http.externalUri | string | `http://127.0.0.1:9000/` | External URI for Graylog |
+| settings.http.publishUri | string | `nil` | Graylog publish URI |
+| settings.clusterName | string | `singlenode-cluster` | Cluster name |
+| settings.javaOpts | string | `nil` | Additional JVM options for Graylog |
+| settings.passwordSecret | string | `somepasswordpepper` | Secret for password encryption and salting |
+| settings.rootUser.username | string | `"admin"` | Graylog root user name |
+| settings.rootUser.sha2password | string | `"8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918"` | Graylog root user password SHA2 (default: "admin") |
+| settings.rootUser.email | string | `""` | Graylog root user email address |
+| settings.rootUser.timezone | string | `"UTC"` | Graylog root user timezone |
+| settings.journal.maxAge | string | `"12h"` | Graylog max. journal age |
+| settings.journal.maxSize | string | `"5gb"` | Graylog max. journal size |
+| settings.elastic.hosts | string | `"http://127.0.0.1:9200"` | Comma separated list of Elasticsearch hosts (only used when `elasticsearch.enabled` is false) |
+| settings.elastic.indexPrefix | string | `"graylog"` | Elasticsearch index prefix |
+| settings.smtp.enabled | bool | `false` | Enable/Disable SMTP |
+| settings.smtp.host | string | `"mail.example.com"` | SMTP host name |
+| settings.smtp.port | int | `587` | SMTP port |
+| settings.smtp.useAuth | bool | `true` | Use SMTP authentication |
+| settings.smtp.useTls | bool | `true` | Use SMTP with STARTTLS |
+| settings.smtp.useSsl | bool | `false` | Enable SMTP over SSL (SMTPS) |
+| settings.smtp.username | string | `"you@example.com"` | SMTP username |
+| settings.smtp.password | string | `"secret"` | SMTP password |
+| settings.smtp.emailFrom | string | `"you@example.com"` | Mail from address |
+| settings.smtp.subjectPrefix | string | `"[graylog]"` | Mail subject prefix |
+
+Further Graylog parameter can be set via environment variables (see Deployment parameter: env)
diff --git a/charts/graylog/charts/elasticsearch-0.1.0.tgz b/charts/graylog/charts/elasticsearch-0.1.0.tgz
new file mode 100644
index 0000000000000000000000000000000000000000..1a614c4f56efed73f57b9908cdd12a7f42871560
Binary files /dev/null and b/charts/graylog/charts/elasticsearch-0.1.0.tgz differ
diff --git a/charts/graylog/charts/mongodb-0.2.7.tgz b/charts/graylog/charts/mongodb-0.2.7.tgz
new file mode 100644
index 0000000000000000000000000000000000000000..0381de981febb699e64e3c1bfb5bfedc3e5d2bf4
Binary files /dev/null and b/charts/graylog/charts/mongodb-0.2.7.tgz differ
diff --git a/charts/graylog/templates/_helpers.tpl b/charts/graylog/templates/_helpers.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..c66cfee7b4e5f76aaff2f821f92d9b73b25ee08f
--- /dev/null
+++ b/charts/graylog/templates/_helpers.tpl
@@ -0,0 +1,169 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "graylog.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 "graylog.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 }}
+
+{{- define "elasticsearch.servicename" -}}
+{{- if .Values.elasticsearch.fullnameOverride }}
+{{- .Values.elasticsearch.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default "elasticsearch" .Values.elasticsearch.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{- define "mongodb.servicename" -}}
+{{- if .Values.mongodb.fullnameOverride }}
+{{- .Values.mongodb.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default "mongodb" .Values.mongodb.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 "graylog.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "graylog.labels" -}}
+helm.sh/chart: {{ include "graylog.chart" . }}
+{{ include "graylog.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "graylog.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "graylog.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "graylog.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "graylog.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
+
+{{/*
+Graylog settings via environment variables
+*/}}
+{{- define "graylog.environment" -}}
+{{- if .Values.elasticsearch.enabled }}
+- name: GRAYLOG_ELASTICSEARCH_HOSTS
+  value: http://{{ include "elasticsearch.servicename" . }}:{{ .Values.elasticsearch.service.httpPort }}
+{{- else }}
+{{- if .Values.settings.elastic.hosts }}
+- name: GRAYLOG_ELASTICSEARCH_HOSTS
+  value: {{ .Values.settings.elastic.hosts | quote }}
+{{- end }}
+{{- end }}   
+{{- if .Values.settings.javaOpts }}
+- name: GRAYLOG_SERVER_JAVA_OPTS
+  value: {{ .Values.settings.javaOpts | quote }}
+{{- end }}
+{{- if .Values.settings.http.externalUri }}
+- name: GRAYLOG_HTTP_EXTERNAL_URI
+  value: {{ .Values.settings.http.externalUri | quote }}
+{{- end }}
+{{- if .Values.settings.http.publishUri }}
+- name: GRAYLOG_HTTP_PUBLISH_URI
+  value: {{ .Values.settings.http.publishUri | quote }}
+{{- end }}
+{{- if .Values.settings.rootUser.email }}
+- name: GRAYLOG_ROOT_EMAIL
+  value: {{ .Values.settings.rootUser.email | quote }}
+{{- end }}
+{{- if .Values.settings.rootUser.timezone }}
+- name: GRAYLOG_ROOT_TIMEZONE
+  value: {{ .Values.settings.rootUser.timezone | quote }}
+{{- end }}
+{{- if .Values.settings.elastic.indexPrefix }}
+- name: GRAYLOG_ELASTICSEARCH_INDEX_PREFIX
+  value: {{ .Values.settings.elastic.indexPrefix | quote }}
+{{- end }}
+{{- if .Values.settings.journal.maxAge }}
+- name: GRAYLOG_MESSAGE_JOURNAL_MAX_AGE
+  value: {{ .Values.settings.journal.maxAge | quote }}
+{{- end }}
+{{- if .Values.settings.journal.maxSize }}
+- name: GRAYLOG_MESSAGE_JOURNAL_MAX_SIZE
+  value: {{ .Values.settings.journal.maxSize | quote }}
+{{- end }}
+{{- if .Values.settings.smtp.enabled }}
+{{- if .Values.settings.smtp.host }}
+- name: GRAYLOG_TRANSPORT_EMAIL_HOSTNAME
+  value: {{ .Values.settings.smtp.host | quote }}
+{{- end }}
+{{- if .Values.settings.smtp.port }}
+- name: GRAYLOG_TRANSPORT_EMAIL_PORT
+  value: {{ .Values.settings.smtp.port | quote }}
+{{- end }}
+{{- if .Values.settings.smtp.useAuth }}
+- name: GRAYLOG_TRANSPORT_EMAIL_USE_AUTH
+  value: {{ .Values.settings.smtp.useAuth | quote }}
+{{- end }}
+{{- if .Values.settings.smtp.useTls }}
+- name: GRAYLOG_TRANSPORT_EMAIL_USE_TLS
+  value: {{ .Values.settings.smtp.useTls | quote }}
+{{- end }}
+{{- if .Values.settings.smtp.useSsl }}
+- name: GRAYLOG_TRANSPORT_EMAIL_USE_SSL
+  value: {{ .Values.settings.smtp.useSsl | quote }}
+{{- end }}
+{{- if .Values.settings.smtp.username }}
+- name: GRAYLOG_TRANSPORT_EMAIL_AUTH_USERNAME
+  value: {{ .Values.settings.smtp.username | quote }}
+{{- end }}
+{{- if .Values.settings.smtp.subjectPrefix }}
+- name: GRAYLOG_TRANSPORT_EMAIL_SUBJECT_PREFIX
+  value: {{ .Values.settings.smtp.subjectPrefix | quote }}
+{{- end }}
+{{- if .Values.settings.smtp.emailFrom }}
+- name: GRAYLOG_TRANSPORT_EMAIL_FROM_EMAIL
+  value: {{ .Values.settings.smtp.emailFrom | quote }}
+{{- end }}
+{{- end }}
+{{- end }}
diff --git a/charts/graylog/templates/extraservices.yaml b/charts/graylog/templates/extraservices.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..cc986300577cad32feb2c1f2b929938287821469
--- /dev/null
+++ b/charts/graylog/templates/extraservices.yaml
@@ -0,0 +1,30 @@
+{{- $fullname := include "graylog.fullname" . }}
+{{- $labels := include "graylog.labels" . }}
+{{- $selectorLabels := include "graylog.selectorLabels" . }}
+{{- range $service := .Values.extraServices }}
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ $fullname }}-{{ $service.name }}
+  labels:
+    {{- $labels | nindent 4 }}
+spec:
+  type: {{ $service.type }}
+  ports:
+    - port: {{ $service.port }}
+      targetPort: {{ $service.name }}
+      protocol: {{ $service.protocol }}
+      name: {{ $service.name }}
+      {{- if and ( or (eq $service.type "LoadBalancer") (eq $service.type "NodePort") ) ($service.nodePort) }}
+      nodePort: {{ $service.nodePort }}
+      {{- end }}
+  {{- if and (eq $service.type "LoadBalancer") ($service.loadBalancerIP) }}
+  loadBalancerIP: {{ $service.loadBalancerIP }}
+  {{- end }}
+  {{- if $service.clusterIP }}
+  clusterIP: {{ $service.clusterIP }}
+  {{- end }}
+  selector:
+    {{- $selectorLabels | nindent 4 }}
+---
+{{- end }}
\ No newline at end of file
diff --git a/charts/graylog/templates/geoipsecrets.yaml b/charts/graylog/templates/geoipsecrets.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..fb8b0ed6d8b04f42a857a0caec24b7ce0bb60b84
--- /dev/null
+++ b/charts/graylog/templates/geoipsecrets.yaml
@@ -0,0 +1,21 @@
+{{- if .Values.initGeoIPDatabase.enabled }}
+apiVersion: v1
+kind: Secret
+metadata:
+  name: {{ include "graylog.fullname" . }}-geoip
+  labels:
+    {{- include "graylog.labels" . | nindent 4 }}
+stringData:
+  GEOIPUPDATE_ACCOUNT_ID: {{ .Values.initGeoIPDatabase.accountId | quote }}
+  GEOIPUPDATE_LICENSE_KEY: {{ .Values.initGeoIPDatabase.licenseKey | quote }}
+  GEOIPUPDATE_EDITION_IDS: {{ .Values.initGeoIPDatabase.editionId | quote }}
+  {{- if .Values.initGeoIPDatabase.host }}
+  GEOIPUPDATE_HOST: {{ .Values.initGeoIPDatabase.host | quote }}
+  {{- end }}
+  {{- if .Values.initGeoIPDatabase.proxy }}
+  GEOIPUPDATE_PROXY: {{ .Values.initGeoIPDatabase.proxy | quote }}
+  {{- end }}
+  {{- if .Values.initGeoIPDatabase.proxyUserPassword }}
+  GEOIPUPDATE_PROXY_USER_PASSWORD: {{ .Values.initGeoIPDatabase.proxyUserPassword | quote }}
+  {{- end }}
+{{- end }}
diff --git a/charts/graylog/templates/ingress.yaml b/charts/graylog/templates/ingress.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..0641f690be025868d1cd7d6e463069443bbe44a4
--- /dev/null
+++ b/charts/graylog/templates/ingress.yaml
@@ -0,0 +1,41 @@
+{{- if .Values.ingress.enabled -}}
+{{- $fullName := include "graylog.fullname" . -}}
+{{- $svcPort := .Values.service.port -}}
+{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1beta1
+{{- else -}}
+apiVersion: extensions/v1beta1
+{{- end }}
+kind: Ingress
+metadata:
+  name: {{ $fullName }}
+  labels:
+    {{- include "graylog.labels" . | nindent 4 }}
+  {{- with .Values.ingress.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
+spec:
+  {{- if .Values.ingress.tls }}
+  tls:
+    {{- range .Values.ingress.tls }}
+    - hosts:
+        {{- range .hosts }}
+        - {{ . | quote }}
+        {{- end }}
+      secretName: {{ .secretName }}
+    {{- end }}
+  {{- end }}
+  rules:
+    {{- range .Values.ingress.hosts }}
+    - host: {{ .host | quote }}
+      http:
+        paths:
+          {{- range .paths }}
+          - path: {{ . }}
+            backend:
+              serviceName: {{ $fullName }}
+              servicePort: {{ $svcPort }}
+          {{- end }}
+    {{- end }}
+  {{- end }}
diff --git a/charts/graylog/templates/scripts.yaml b/charts/graylog/templates/scripts.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..d6b59303d1c85e5f78e9ad9922fd107d81c4ca1b
--- /dev/null
+++ b/charts/graylog/templates/scripts.yaml
@@ -0,0 +1,18 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: {{ include "graylog.fullname" . }}-scripts
+  labels:
+    {{- include "graylog.labels" . | nindent 4 }}
+data:
+  init.sh: |
+    #!/bin/bash
+    if [ ! -f /data/config/graylog.conf ]; then
+      mkdir -p /data/config
+      mkdir -p /data/journal
+      mkdir -p /data/log
+      mkdir -p /data/plugin
+      mkdir -p /data/contentpacks
+      cp /usr/share/graylog/data/config/* /data/config
+      chmod u+rw /data/config/*
+    fi
diff --git a/charts/graylog/templates/secureconfig.yaml b/charts/graylog/templates/secureconfig.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..8c42b4001dcb0b4fba3dfbb2d21dfb871e098aa8
--- /dev/null
+++ b/charts/graylog/templates/secureconfig.yaml
@@ -0,0 +1,26 @@
+apiVersion: v1
+kind: Secret
+metadata:
+  name: {{ include "graylog.fullname" . }}
+  labels:
+    {{- include "graylog.labels" . | nindent 4 }}
+stringData:
+{{- if .Values.mongodb.enabled }}
+  GRAYLOG_MONGODB_URI: mongodb://{{ .Values.mongodb.userDatabase.user}}:{{ .Values.mongodb.userDatabase.password}}@{{ include "mongodb.servicename" . }}:{{ .Values.mongodb.service.port }}/{{ .Values.mongodb.userDatabase.name }}
+{{- else }}
+  {{- with .Values.externalDatabase }}
+  GRAYLOG_MONGODB_URI: mongodb://{{ .user }}:{{ .password }}@{{ .host }}/{{ .name }}
+  {{- end }}
+{{- end }}
+{{- if and (.Values.settings.smtp.enabled) (.Values.settings.smtp.password) }}
+  GRAYLOG_TRANSPORT_EMAIL_AUTH_PASSWORD: {{ .Values.settings.smtp.password | quote }}
+{{- end }}
+{{- if .Values.settings.passwordSecret }}
+  GRAYLOG_PASSWORD_SECRET: {{ .Values.settings.passwordSecret | quote }}
+{{- end }}
+{{- if .Values.settings.rootUser.username }}
+  GRAYLOG_ROOT_USERNAME: {{ .Values.settings.rootUser.username | quote }}
+{{- end }}
+{{- if .Values.settings.rootUser.sha2Password }}
+  GRAYLOG_ROOT_PASSWORD_SHA2: {{ .Values.settings.rootUser.sha2Password | quote }}
+{{- end }}
diff --git a/charts/graylog/templates/service-internal.yaml b/charts/graylog/templates/service-internal.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..319f8127838bab2524af81e77fe2ff50a4a07458
--- /dev/null
+++ b/charts/graylog/templates/service-internal.yaml
@@ -0,0 +1,16 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ include "graylog.fullname" . }}-internal
+  labels:
+    {{- include "graylog.labels" . | nindent 4 }}
+spec:
+  type: ClusterIP
+  clusterIP: None
+  ports:
+    - port: {{ .Values.service.port }}
+      targetPort: http
+      protocol: TCP
+      name: http
+  selector:
+    {{- include "graylog.selectorLabels" . | nindent 4 }}
diff --git a/charts/graylog/templates/service.yaml b/charts/graylog/templates/service.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..6598409ddee139ab684fb09dc8838836c92aa130
--- /dev/null
+++ b/charts/graylog/templates/service.yaml
@@ -0,0 +1,24 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ include "graylog.fullname" . }}
+  labels:
+    {{- include "graylog.labels" . | nindent 4 }}
+spec:
+  type: {{ .Values.service.type }}
+  ports:
+    - port: {{ .Values.service.port }}
+      targetPort: http
+      protocol: TCP
+      name: http
+      {{- if and ( or (eq .Values.service.type "LoadBalancer") (eq .Values.service.type "NodePort") ) (.Values.service.nodePort) }}
+      nodePort: {{ .Values.service.nodePort }}
+      {{- end }}
+  {{- if and (eq .Values.service.type "LoadBalancer") (.Values.service.loadBalancerIP) }}
+  loadBalancerIP: {{ .Values.service.loadBalancerIP }}
+  {{- end }}
+  {{- if .Values.service.clusterIP }}
+  clusterIP: {{ .Values.service.clusterIP }}
+  {{- end }}
+  selector:
+    {{- include "graylog.selectorLabels" . | nindent 4 }}
diff --git a/charts/graylog/templates/serviceaccount.yaml b/charts/graylog/templates/serviceaccount.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..9eaa278e3c28210c749756b9655ccbc8e30afccf
--- /dev/null
+++ b/charts/graylog/templates/serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: {{ include "graylog.serviceAccountName" . }}
+  labels:
+    {{- include "graylog.labels" . | nindent 4 }}
+  {{- with .Values.serviceAccount.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
+{{- end }}
diff --git a/charts/graylog/templates/statefulset.yaml b/charts/graylog/templates/statefulset.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..7e176bbc1e6ff5500ee541909e47bcd90c3f9a76
--- /dev/null
+++ b/charts/graylog/templates/statefulset.yaml
@@ -0,0 +1,189 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: {{ include "graylog.fullname" . }}
+  labels:
+    {{- include "graylog.labels" . | nindent 4 }}
+spec:
+  selector:
+    matchLabels:
+      {{- include "graylog.selectorLabels" . | nindent 6 }}
+  serviceName: {{ include "graylog.fullname" . }}-internal
+  podManagementPolicy: {{ .Values.podManagementPolicy }}      
+  replicas: {{ .Values.replicaCount }}
+  updateStrategy: 
+    type: {{ .Values.updateStrategyType }}    
+  template:
+    metadata:
+    {{- with .Values.podAnnotations }}
+      annotations:
+        {{- toYaml . | nindent 8 }}
+    {{- end }}
+      labels:
+        {{- include "graylog.selectorLabels" . | nindent 8 }}
+    spec:
+      {{- with .Values.imagePullSecrets }}
+      imagePullSecrets:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      serviceAccountName: {{ include "graylog.serviceAccountName" . }}
+      {{- with .Values.podSecurityContext }}
+      securityContext:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      initContainers:
+        {{- if .Values.initGeoIPDatabase.enabled }}
+        - name: {{ .Chart.Name }}-geocitydbinit
+          {{- with .Values.securityContext }}
+          securityContext:
+            allowPrivilegeEscalation: false
+            privileged: false
+          {{- end }}
+          image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}"
+          imagePullPolicy: {{ .Values.initImage.pullPolicy }}
+          envFrom:
+            - secretRef:
+                name: {{ include "graylog.fullname" . }}-geoip
+          volumeMounts:
+            - name: geocity
+              mountPath: /usr/share/GeoIP
+        {{- end }}
+        - name: {{ .Chart.Name }}-init
+          {{- with .Values.securityContext }}
+          securityContext:
+            {{- toYaml . | nindent 12 }}
+          {{- end }}
+          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+          imagePullPolicy: {{ .Values.image.pullPolicy }}
+          command: [ "/scripts/init.sh" ]
+          volumeMounts:
+            - name: scripts
+              mountPath: /scripts
+            - name: graylog-volume
+              mountPath: /data
+      containers:
+        - name: {{ .Chart.Name }}
+          {{- with .Values.securityContext }}
+          securityContext:
+            {{- toYaml . | nindent 12 }}
+          {{- end }}
+          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+          imagePullPolicy: {{ .Values.image.pullPolicy }}
+          ports:
+            - name: http
+              containerPort: 9000
+              protocol: TCP
+            {{- range $service := .Values.extraServices }}
+            - name: {{ $service.name }}
+              containerPort: {{ $service.containerPort}}
+              protocol: {{ $service.protocol }}
+            {{- end }}
+          envFrom:
+            - secretRef:
+                name: {{ include "graylog.fullname" . }}
+          env:
+            - name: POD_NAME
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.name               
+            {{- include "graylog.environment" . | nindent 12 }}
+          {{- with .Values.env }}
+            {{- toYaml . | nindent 12 }}
+          {{- end }}          
+          {{- if .Values.livenessProbe.enabled }}
+          livenessProbe:
+            httpGet:
+              path: /api/system/lbstatus
+              port: http
+              httpHeaders:
+                - name: Host
+                  value: localhost:9000
+            {{- with .Values.livenessProbe }}
+            initialDelaySeconds: {{ .initialDelaySeconds }}
+            timeoutSeconds: {{ .timeoutSeconds }}
+            failureThreshold: {{ .failureThreshold }}
+            successThreshold: {{ .successThreshold }}
+            periodSeconds: {{ .periodSeconds }}
+            {{- end }}
+          {{- end }}
+          {{- if .Values.readinessProbe.enabled }}
+          readinessProbe:
+            httpGet:
+              path: /api/system/lbstatus
+              port: http
+              httpHeaders:
+                - name: Host
+                  value: localhost:9000
+            {{- with .Values.readinessProbe }}
+            initialDelaySeconds: {{ .initialDelaySeconds }}
+            timeoutSeconds: {{ .timeoutSeconds }}
+            failureThreshold: {{ .failureThreshold }}
+            successThreshold: {{ .successThreshold }}
+            periodSeconds: {{ .periodSeconds }}
+            {{- end }}
+          {{- end }}
+          {{- with .Values.resources }}
+          resources:
+            {{- toYaml . | nindent 12 }}
+          {{- end }}
+          {{- if .Values.args }}
+          args:
+            {{- range .Values.args }}
+            - {{ . }}
+            {{- end }}
+          {{- end }}
+          volumeMounts:
+            - mountPath: /etc/graylog/server
+              name: geocity
+            - mountPath: /tmp
+              name: tmp
+            - mountPath: /usr/share/graylog/data
+              name: graylog-volume
+      {{- with .Values.nodeSelector }}
+      nodeSelector:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.affinity }}
+      affinity:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.tolerations }}
+      tolerations:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      volumes:
+        - name: tmp
+          emptyDir: {}
+        - name: geocity
+          emptyDir: {}
+        - name: scripts
+          configMap:
+            name: {{ include "graylog.fullname" . }}-scripts
+            defaultMode: 0555          
+  {{- if .Values.storage.persistentVolumeClaimName }}
+        - name: graylog-volume
+          persistentVolumeClaim:
+            claimName: {{ .Values.storage.persistentVolumeClaimName }}
+  {{- else }}
+  {{- if not .Values.storage.requestedSize }}
+        - name: graylog-volume
+          emptyDir: {}
+  {{- else }}
+  volumeClaimTemplates:
+    - metadata:
+        name: graylog-volume
+        labels:
+          {{- include "graylog.labels" . | nindent 10 }}
+      spec:
+        {{- with .Values.storage.accessModes }}
+        accessModes:
+          {{- toYaml . | nindent 10 }}
+        {{- end }}
+        {{- if .Values.storage.className }}
+        storageClassName: {{ .Values.storage.className }}
+        {{- end }}
+        resources:
+          requests:
+            storage: {{ .Values.storage.requestedSize }}
+  {{- end }}
+  {{- end }}
\ No newline at end of file
diff --git a/charts/graylog/templates/tests/test-connection.yaml b/charts/graylog/templates/tests/test-connection.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ce5105a3585c598aabe9fd9d1ff7244d7417975e
--- /dev/null
+++ b/charts/graylog/templates/tests/test-connection.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: "{{ include "graylog.fullname" . }}-test-connection"
+  labels:
+    {{- include "graylog.labels" . | nindent 4 }}
+  annotations:
+    "helm.sh/hook": test-success
+spec:
+  containers:
+    - name: wget
+      image: busybox
+      command: ['wget']
+      args: ['{{ include "graylog.fullname" . }}:{{ .Values.service.port }}']
+  restartPolicy: Never
diff --git a/charts/graylog/values.yaml b/charts/graylog/values.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..4b896b21817fe865248bfa9b0e072a0f12eede6a
--- /dev/null
+++ b/charts/graylog/values.yaml
@@ -0,0 +1,288 @@
+## Default values for Graylog deployment
+
+## Graylog docker image
+image:
+  repository: graylog/graylog
+  pullPolicy: IfNotPresent
+  # Overrides the image tag whose default is the chart appVersion.
+  tag: ""
+
+# Default Init container image
+initImage:
+  repository: maxmindinc/geoipupdate
+  pullPolicy: IfNotPresent
+  tag: "latest"
+
+imagePullSecrets: []
+nameOverride: ""
+fullnameOverride: ""
+
+## Optional service account
+serviceAccount:
+  # Specifies whether a service account should be created
+  create: false
+  # Annotations to add to the service account
+  annotations: {}
+  # The name of the service account to use.
+  # If not set and create is true, a name is generated using the fullname template
+  name: ""
+
+podAnnotations: {}
+
+## Management policy, update strategy and number of replicas
+podManagementPolicy: OrderedReady
+updateStrategyType: RollingUpdate
+replicaCount: 1
+
+## Pod security options (default: Graylog as fsGroup)
+podSecurityContext:
+  fsGroup: 1100
+
+## Default security options to run Graylog as read only container without privilege escalation
+securityContext:
+  allowPrivilegeEscalation: false
+  privileged: false
+  readOnlyRootFilesystem: true
+  runAsNonRoot: true
+  runAsGroup: 1100
+  runAsUser: 1100
+
+## Default Graylog service port (default web service port)
+service:
+  type: ClusterIP
+  port: 80
+  ## The node port (only relevant for type LoadBalancer or NodePort)
+  nodePort:
+  ## The cluster ip address (only relevant for type LoadBalancer or NodePort)
+  clusterIP:
+  ## The loadbalancer ip address (only relevant for type LoadBalancer)
+  loadBalancerIP:
+
+## List of extra service ports
+extraServices: {}
+#  - name: udpinput
+    ## Service port type
+#    type: ClusterIP
+    ## Service protocol
+#    protocol: UDP
+    ## The container target port
+#    containerPort: 12001
+    ## The service port
+#    port: 12001
+    ## The node port (only relevant for type LoadBalancer or NodePort)
+#    nodePort:
+    ## The cluster ip address (only relevant for type LoadBalancer or NodePort)
+#    clusterIP:
+    ## The loadbalancer ip address (only relevant for type LoadBalancer)
+#    loadBalancerIP:
+
+## Ingress configuration
+ingress:
+  ## Enable ingress endpoint
+  enabled: false
+
+  ## Additional ingress annotations
+  annotations: {}
+    # kubernetes.io/ingress.class: nginx
+    # kubernetes.io/tls-acme: "true"
+
+  ## Hosts
+  hosts:
+    - host:
+      paths: []
+
+  ## TLS settings for hosts
+  tls: []
+  #  - secretName: chart-example-tls
+  #    hosts:
+  #      - chart-example.local
+
+## Resource limits and requests
+resources: {}
+  # limits:
+  #   cpu: 100m
+  #   memory: 128Mi
+  # requests:
+  #   cpu: 100m
+  #   memory: 128Mi
+
+## Additional node selector
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}
+
+## Default liveness probe
+livenessProbe:
+  enabled: true
+  initialDelaySeconds: 30
+  timeoutSeconds: 5
+  failureThreshold: 3
+  successThreshold: 1
+  periodSeconds: 10
+
+## Default readiness probe
+readinessProbe:
+  enabled: true
+  initialDelaySeconds: 30
+  timeoutSeconds: 5
+  failureThreshold: 3
+  successThreshold: 1
+  periodSeconds: 10
+
+## Additional environment variables
+env: []
+
+## Arguments for the container entrypoint process
+args: []
+
+## Reference to external MongoDB database
+externalDatabase:
+  ## Name of the database (default: graylog)
+  name: "graylog"
+
+  ## Database user
+  user:
+
+  ## Database password
+  password:
+
+  ## Database host
+  host:
+
+## Optinal step to enable and initialize the GeoIP database file (for more info see: https://dev.maxmind.com/geoip/geoipupdate/)
+initGeoIPDatabase:
+  ## Enable GeoIP database download
+  enabled: false
+  ## MaxMind AccountId/UserId
+  accountId: ""
+  ## MaxMind license key
+  licenseKey: ""
+  ## GeoLite database edition (https://www.maxmind.com/en/accounts/473747/geoip/downloads)
+  editionId: "GeoLite2-City"
+
+  ## The MaxMind download host (not necessary to change that)
+  host:
+  ## A valid proxy if internet connection is running through a proxy
+  proxy:
+  ## Proxy username and password in format "username:password"
+  proxyUserPassword:
+
+## Graylog specific settings
+settings:
+  http:
+    externalUri: "http://127.0.0.1:9000/"
+    publishUri:
+
+  javaOpts:
+  passwordSecret: "somepasswordpepper"
+
+  rootUser:
+    username: "admin"
+    sha2Password: "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918"
+    email: ""
+    timezone: "UTC"
+
+  journal:
+    maxAge: "12h"
+    maxSize: "5gb"
+
+  elastic:
+    hosts: "http://127.0.0.1:9200"
+    indexPrefix: "graylog"
+
+  smtp:
+    enabled: false
+    host: "mail.example.com"
+    port: 587
+    useAuth: true
+    useTls: true
+    useSsl: false
+    username: "you@example.com"
+    password: "secret"
+    emailFrom: "you@example.com"
+    subjectPrefix: "[graylog]"
+
+## Storage parameters for Graylog journal
+storage:
+  ##  Set persistentVolumenClaimName to reference an existing PVC
+  persistentVolumeClaimName:
+
+  ## Alternative set requestedSize to define a size for a dynmaically created PVC
+  requestedSize:
+
+  ## the storage class name
+  className:
+
+  ## Default access mode (ReadWriteOnce)
+  accessModes:
+    - ReadWriteOnce
+
+## MongoDB configuration
+mongodb:
+  ## Enable MongoDB helm chart for deployment (default: false)
+  enabled: false
+
+  ## Database settings
+  settings:
+    ## The root username (default: admin)
+    rootUsername: "admin"
+    ## The root user password (default: a 10 char. alpahnumerical random password will be generated)
+    rootPassword:
+
+  ## Optional user database which is created during first startup with user and password
+  userDatabase: {}
+    ## Name of the user database
+    # name:
+
+    ## Database user with full access rights
+    # user:
+
+    ## Password of the database user (default: a 10 char. alpahnumerical random password will be generated)
+    # password:
+
+  ## Custom configuration (use instead of command args)
+  customConfig: |
+
+  # Storage parameters
+  # When a persistentVolumenClaimName is configured the requestedSize and accessModes will be ignored and the existing PVC based on the name will be used
+  # otherwise a dynamic persistent volume will be created with PVC name of this chart and the configured requestedSize and accessModes
+  # If both parameters are missing a volatile emtpyDir volume will be used as Graylog storage
+  storage:
+    ## Set persistentVolumenClaimName to reference an existing PVC
+    persistentVolumeClaimName:
+
+    ## Alternative set requestedSize to define a size for a dynamically created PVC
+    requestedSize:
+
+    ## the storage class name
+    className:
+
+    ## Default access mode (ReadWriteOnce)
+    accessModes:
+      - ReadWriteOnce
+
+## Elasticsearch configuration
+elasticsearch:
+  ## Enable Elasticsearch helm chart for deployment (default: false)
+  enabled: false
+
+  settings:
+    javaOpts: "-Xms512m -Xmx512m"
+    clusterName: "graylog"
+
+  ## Storage parameters for Elasticsearch database
+  storage:
+    ##  Set persistentVolumenClaimName to reference an existing PVC
+    persistentVolumeClaimName:
+
+    ## Alternative set requestedSize to define a size for a dynmaically created PVC
+    requestedSize:
+
+    ## the storage class name
+    className:
+
+    ## Default access mode (ReadWriteOnce)
+    accessModes:
+      - ReadWriteOnce