diff --git a/cgat b/cgat
new file mode 100755
index 0000000000000000000000000000000000000000..7437e90723a9340dd1e3872e78ba87ef32c949b1
Binary files /dev/null and b/cgat differ
diff --git a/cgat.sh b/cgat.sh
deleted file mode 100755
index 9e455f717f9609e2c7d1d0416606f46c24bdb104..0000000000000000000000000000000000000000
--- a/cgat.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-
-openssl req -nodes -new -x509 -subj '/CN=spilo.dummy.org' -keyout /etc/ssl/certs/pgbouncer.key -out /etc/ssl/certs/pgbouncer.crt
-/bin/pggat
diff --git a/cmd/cgat/main.go b/cmd/cgat/main.go
index e5b383b97317d85522c4e412616421236166cef3..5e2d58401846e34bcc475d1ad919dda61da8f878 100644
--- a/cmd/cgat/main.go
+++ b/cmd/cgat/main.go
@@ -1,7 +1,6 @@
 package main
 
 import (
-	"fmt"
 	"net/http"
 	_ "net/http/pprof"
 	"os"
@@ -20,77 +19,41 @@ func main() {
 		panic(http.ListenAndServe(":8080", nil))
 	}()
 
-	log.Printf("Starting pggat...")
-
-	if len(os.Args) == 2 {
-		log.Printf("running in pgbouncer compatibility mode")
-		conf, err := pgbouncer.Load(os.Args[1])
-		if err != nil {
-			panic(err)
-		}
-
-		err = conf.ListenAndServe()
-		if err != nil {
-			panic(err)
-		}
-		return
+	runMode := os.Getenv("PGGAT_RUN_MODE")
+	if runMode == "" {
+		runMode = "pgbouncer"
 	}
 
-	if os.Getenv("CONNECTION_POOLER_MODE") != "" {
-		log.Printf("running in zalando compatibility mode")
-
-		conf, err := zalando.Load()
-		if err != nil {
-			panic(err)
-		}
+	log.Printf("Starting pggat (%s)...", runMode)
 
-		err = conf.ListenAndServe()
-		if err != nil {
-			panic(err)
-		}
-		return
+	// TODO: this really should load a dynamically registered module
+	var conf interface {
+		ListenAndServe() error
 	}
-
-	if os.Getenv("PGGAT_GC_PROJECT") != "" {
-		conf, err := cloud_sql_discovery.Load()
-		if err != nil {
-			panic(err)
-		}
-		err = conf.ListenAndServe()
-		if err != nil {
-			panic(err)
-		}
-		return
+	var err error
+	switch runMode {
+	case "pggat":
+		conf, err = pgbouncer.Load(os.Args[1])
+	case "pgbouncer":
+		conf, err = pgbouncer.Load(os.Args[1])
+	case "pgbouncer_spilo":
+		conf, err = zalando.Load()
+	case "zalando_kubernetes_operator":
+		conf, err = zalando_operator_discovery.Load()
+	case "google_cloud_sql":
+		conf, err = cloud_sql_discovery.Load()
+	case "digitalocean_databases":
+		conf, err = digitalocean_discovery.Load()
+	default:
+		panic("Unknown PGGAT_RUN_MODE: " + runMode)
 	}
-
-	if os.Getenv("PGGAT_DO_API_KEY") != "" {
-		log.Printf("running in digitalocean discovery mode")
-
-		conf, err := digitalocean_discovery.Load()
-		if err != nil {
-			panic(err)
-		}
-
-		err = conf.ListenAndServe()
-		if err != nil {
-			panic(err)
-		}
-		return
+	if err != nil {
+		panic(err)
 	}
 
-	if os.Getenv("KUBERNETES_SERVICE_HOST") != "" && os.Getenv("KUBERNETES_SERVICE_PORT") != "" {
-		log.Printf("running in zalando operator discovery mode")
-		conf, err := zalando_operator_discovery.Load()
-		if err != nil {
-			panic(err)
-		}
-
-		err = conf.ListenAndServe()
-		if err != nil {
-			panic(err)
-		}
-		return
+	err = conf.ListenAndServe()
+	if err != nil {
+		panic(err)
 	}
-
-	panic(fmt.Sprintf("usage: %s <config>", os.Args[0]))
+	return
 }
diff --git a/entrypoint.sh b/entrypoint.sh
new file mode 100755
index 0000000000000000000000000000000000000000..61f26ffc99437cabcc2421fb715dfa125df0f63e
--- /dev/null
+++ b/entrypoint.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+set -e
+
+EXEC_BIN_PATH=${PGGAT_BIN_PATH:=/usr/bin/pggat}
+
+pggat() {
+    exec $EXEC_BIN_PATH ${@}
+}
+
+
+case "${1}" in
+    "")
+        pggat ${@}
+        ;;
+    *)
+        export PGGAT_RUN_MODE=${1}
+        shift
+        pggat ${@}
+        ;;
+esac
diff --git a/lib/gat/modes/cloud_sql_discovery/config.go b/lib/gat/modes/cloud_sql_discovery/config.go
index da7df4fdf5b529927be8fae763c13cd797023929..369b4ffd9ca592ea800f3f0cfcfab25cafa8b265 100644
--- a/lib/gat/modes/cloud_sql_discovery/config.go
+++ b/lib/gat/modes/cloud_sql_discovery/config.go
@@ -21,13 +21,13 @@ type Config struct {
 	AuthPassword  string `env:"PGGAT_GC_AUTH_PASSWORD"`
 }
 
-func Load() (Config, error) {
+func Load() (*Config, error) {
 	var conf Config
 	gun.Load(&conf)
 	if conf.Project == "" {
-		return Config{}, errors.New("expected google cloud project id")
+		return &Config{}, errors.New("expected google cloud project id")
 	}
-	return conf, nil
+	return &conf, nil
 }
 
 func (T *Config) ListenAndServe() error {
diff --git a/lib/gat/modes/digitalocean_discovery/config.go b/lib/gat/modes/digitalocean_discovery/config.go
index ddf416577b32c468ce5b694ed1a488dceff8f9a4..e476286da1a3d460b8245605c2af176e30bd3462 100644
--- a/lib/gat/modes/digitalocean_discovery/config.go
+++ b/lib/gat/modes/digitalocean_discovery/config.go
@@ -35,14 +35,14 @@ type Config struct {
 	TLSKeyFile string `env:"PGGAT_TLS_KEY_FILE" default:"/etc/ssl/certs/pgbouncer.key"`
 }
 
-func Load() (Config, error) {
+func Load() (*Config, error) {
 	var conf Config
 	gun.Load(&conf)
 	if conf.APIKey == "" {
-		return Config{}, errors.New("expected auth token")
+		return &Config{}, errors.New("expected auth token")
 	}
 
-	return conf, nil
+	return &conf, nil
 }
 
 func (T *Config) ListenAndServe() error {
diff --git a/lib/gat/modes/pgbouncer/config.go b/lib/gat/modes/pgbouncer/config.go
index 191ba398a2b860416f042c4223e317d48d22944b..648c34f0b8816686ffb4057f4534e6e704867f2d 100644
--- a/lib/gat/modes/pgbouncer/config.go
+++ b/lib/gat/modes/pgbouncer/config.go
@@ -241,15 +241,15 @@ var Default = Config{
 	},
 }
 
-func Load(config string) (Config, error) {
+func Load(config string) (*Config, error) {
 	conf, err := ini.ReadFile(config)
 	if err != nil {
-		return Config{}, err
+		return &Config{}, err
 	}
 
 	var c = Default
 	err = ini.Unmarshal(conf, &c)
-	return c, err
+	return &c, err
 }
 
 func (T *Config) ListenAndServe() error {
diff --git a/lib/gat/modes/zalando/config.go b/lib/gat/modes/zalando/config.go
index 44b3e255986aedf3bdb546ba6bab3bb9ca8f2676..a0812e3697fd0c656f0450eb0421d8564c8445d1 100644
--- a/lib/gat/modes/zalando/config.go
+++ b/lib/gat/modes/zalando/config.go
@@ -26,14 +26,14 @@ type Config struct {
 	PoolerMaxDBConn     int    `env:"CONNECTION_POOLER_MAX_DB_CONN"`
 }
 
-func Load() (Config, error) {
+func Load() (*Config, error) {
 	var conf Config
 	gun.Load(&conf)
 	if conf.PoolerMode == "" {
-		return Config{}, errors.New("expected pooler mode")
+		return &Config{}, errors.New("expected pooler mode")
 	}
 
-	return conf, nil
+	return &conf, nil
 }
 
 func (T *Config) ListenAndServe() error {
diff --git a/pggat.Dockerfile b/pggat.Dockerfile
index 3e562d5f61b981a2f359979c538cac0d46943f8b..57061010df72e342af9468b6d92544448a2b58e6 100644
--- a/pggat.Dockerfile
+++ b/pggat.Dockerfile
@@ -8,16 +8,12 @@ RUN go mod tidy
 RUN go build -o cgat ./cmd/cgat
 
 FROM alpine:latest
+WORKDIR /
+RUN apk add --no-cache bash
 
-WORKDIR /bin
+COPY entrypoint.sh .
 
-RUN addgroup -S pgbouncer && adduser -S pgbouncer
-COPY --from=GOBUILDER /src/cgat.sh entrypoint.sh
-COPY --from=GOBUILDER /src/cgat pggat
-RUN apk add openssl
-RUN install -d -m 0755 -o pgbouncer -g pgbouncer /etc/pgbouncer /var/log/pgbouncer /var/run/pgbouncer /etc/ssl/certs
-RUN chown -R pgbouncer:pgbouncer /bin/entrypoint.sh
-RUN cp /bin/entrypoint.sh /bin/run.sh
-USER pgbouncer:pgbouncer
+COPY --from=GOBUILDER /src/cgat /usr/bin/pggat
 
-ENTRYPOINT ["entrypoint.sh"]
+ENTRYPOINT ["/entrypoint.sh"]
+cmd ["pggat"]