diff --git a/charts/postgres-operator/templates/deployment.yaml b/charts/postgres-operator/templates/deployment.yaml
index 2d8eebcb32e467785cea46ac49a15beb3ef25e7a..9841bf1bcf73476885365a3ac61a2054c5c56067 100644
--- a/charts/postgres-operator/templates/deployment.yaml
+++ b/charts/postgres-operator/templates/deployment.yaml
@@ -37,6 +37,10 @@ spec:
         image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}"
         imagePullPolicy: {{ .Values.image.pullPolicy }}
         env:
+      {{- if .Values.enableJsonLogging }}
+        - name: ENABLE_JSON_LOGGING
+          value: "true"
+      {{- end }}
       {{- if eq .Values.configTarget "ConfigMap" }}
         - name: CONFIG_MAP_NAME
           value: {{ template "postgres-operator.fullname" . }}
diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml
index 37eac425418ade7956142565e98de6deecc79ef5..d4acfe1aac31f04f49862632c9e8b50cb3e23e3f 100644
--- a/charts/postgres-operator/values.yaml
+++ b/charts/postgres-operator/values.yaml
@@ -15,6 +15,9 @@ podLabels: {}
 
 configTarget: "ConfigMap"
 
+# JSON logging format
+enableJsonLogging: false
+
 # general configuration parameters
 configGeneral:
   # choose if deployment creates/updates CRDs with OpenAPIV3Validation
diff --git a/cmd/main.go b/cmd/main.go
index a178c187ed16b9cb150041e909c8e2e87ef93b97..376df0bad53459fb508c4da4a3aedc46b6aa1dab 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -2,7 +2,7 @@ package main
 
 import (
 	"flag"
-	"log"
+	log "github.com/sirupsen/logrus"
 	"os"
 	"os/signal"
 	"sync"
@@ -36,6 +36,8 @@ func init() {
 	flag.BoolVar(&config.NoTeamsAPI, "noteamsapi", false, "Disable all access to the teams API")
 	flag.Parse()
 
+	config.EnableJsonLogging = os.Getenv("ENABLE_JSON_LOGGING") == "true"
+
 	configMapRawName := os.Getenv("CONFIG_MAP_NAME")
 	if configMapRawName != "" {
 
@@ -63,6 +65,9 @@ func init() {
 func main() {
 	var err error
 
+	if config.EnableJsonLogging {
+		log.SetFormatter(&log.JSONFormatter{})
+	}
 	log.SetOutput(os.Stdout)
 	log.Printf("Spilo operator %s\n", version)
 
diff --git a/docs/reference/command_line_and_environment.md b/docs/reference/command_line_and_environment.md
index ece29b094fe270aac6eca7fd7487c2fb81d49e54..35f47cabfe34d42365405b9896984a3d1b79c815 100644
--- a/docs/reference/command_line_and_environment.md
+++ b/docs/reference/command_line_and_environment.md
@@ -56,3 +56,7 @@ The following environment variables are accepted by the operator:
 * **CRD_READY_WAIT_INTERVAL**
   defines the  interval between consecutive attempts waiting for the
   `postgresql` CRD to be created. The default is 5s.
+  
+* **ENABLE_JSON_LOGGING**
+  Set to `true` for JSON formatted logging output.
+  The default is false.
diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go
index aa996288c123add69f75610f85e2b4c5100f2ba6..8e9f020295c713cd5c86cc891fe7648e843ece86 100644
--- a/pkg/controller/controller.go
+++ b/pkg/controller/controller.go
@@ -71,6 +71,9 @@ type Controller struct {
 // NewController creates a new controller
 func NewController(controllerConfig *spec.ControllerConfig, controllerId string) *Controller {
 	logger := logrus.New()
+	if controllerConfig.EnableJsonLogging {
+		logger.SetFormatter(&logrus.JSONFormatter{})
+	}
 
 	var myComponentName = "postgres-operator"
 	if controllerId != "" {
diff --git a/pkg/spec/types.go b/pkg/spec/types.go
index 7a2c0ddac216cdc59928776ba5bd5bb36de60235..78c79e1b32a1e0776c3d4e7e88abcf93956b7762 100644
--- a/pkg/spec/types.go
+++ b/pkg/spec/types.go
@@ -114,6 +114,8 @@ type ControllerConfig struct {
 	CRDReadyWaitTimeout  time.Duration
 	ConfigMapName        NamespacedName
 	Namespace            string
+
+	EnableJsonLogging bool
 }
 
 // cached value for the GetOperatorNamespace