diff --git a/backend/controller/pubsub/integration_test.go b/backend/controller/pubsub/integration_test.go
index bd6893ac17..df80003233 100644
--- a/backend/controller/pubsub/integration_test.go
+++ b/backend/controller/pubsub/integration_test.go
@@ -65,13 +65,13 @@ func TestConsumptionDelay(t *testing.T) {
 				SELECT created_at, ROW_NUMBER() OVER (ORDER BY created_at) AS row_num
 				FROM (
 				select * from topic_events order by created_at
-				)
+				) AS sub_event_times
 			),
 			async_call_times AS (
 				SELECT created_at, ROW_NUMBER() OVER (ORDER BY created_at) AS row_num
 				FROM (
 				select * from async_calls ac order by created_at
-				)
+				) AS sub_async_calls
 			)
 			SELECT COUNT(*)
 			FROM event_times
diff --git a/cmd/ftl/cmd_dev.go b/cmd/ftl/cmd_dev.go
index e596d84fa3..79427253b6 100644
--- a/cmd/ftl/cmd_dev.go
+++ b/cmd/ftl/cmd_dev.go
@@ -47,7 +47,7 @@ func (d *devCmd) Run(ctx context.Context, projConfig projectconfig.Config) error
 	}
 
 	if d.InitDB {
-		dsn, err := d.ServeCmd.setupDB(ctx)
+		dsn, err := d.ServeCmd.setupDB(ctx, d.ServeCmd.DatabaseImage)
 		if err != nil {
 			return fmt.Errorf("failed to setup database: %w", err)
 		}
diff --git a/cmd/ftl/cmd_schema_import.go b/cmd/ftl/cmd_schema_import.go
index 3ce698fc36..917967b46e 100644
--- a/cmd/ftl/cmd_schema_import.go
+++ b/cmd/ftl/cmd_schema_import.go
@@ -136,7 +136,7 @@ func query(ctx context.Context, prompt string) error {
 func (s *schemaImportCmd) setup(ctx context.Context) error {
 	logger := log.FromContext(ctx)
 
-	exists, err := container.DoesExist(ctx, ollamaContainerName)
+	exists, err := container.DoesExist(ctx, ollamaContainerName, optional.None[string]())
 	if err != nil {
 		return err
 	}
diff --git a/cmd/ftl/cmd_serve.go b/cmd/ftl/cmd_serve.go
index 001de80f35..1f985915fe 100644
--- a/cmd/ftl/cmd_serve.go
+++ b/cmd/ftl/cmd_serve.go
@@ -44,6 +44,7 @@ type serveCmd struct {
 	Stop                bool                 `help:"Stop the running FTL instance. Can be used with --background to restart the server" default:"false"`
 	StartupTimeout      time.Duration        `help:"Timeout for the server to start up." default:"1m"`
 	ObservabilityConfig observability.Config `embed:"" prefix:"o11y-"`
+	DatabaseImage       string               `help:"The container image to start for the database" default:"postgres:15.4" env:"FTL_DATABASE_IMAGE" hidden:""`
 	controller.CommonConfig
 }
 
@@ -87,7 +88,7 @@ func (s *serveCmd) run(ctx context.Context, projConfig projectconfig.Config, ini
 	logger.Infof("Starting FTL with %d controller(s)", s.Controllers)
 
 	// Bring up the DB and DAL.
-	dsn, err := s.setupDB(ctx)
+	dsn, err := s.setupDB(ctx, s.DatabaseImage)
 	if err != nil {
 		return err
 	}
@@ -299,13 +300,13 @@ func isServeRunning(logger *log.Logger) (bool, error) {
 	return true, nil
 }
 
-func (s *serveCmd) setupDB(ctx context.Context) (string, error) {
+func (s *serveCmd) setupDB(ctx context.Context, image string) (string, error) {
 	logger := log.FromContext(ctx)
 
 	recreate := s.Recreate
 	port := s.DBPort
 
-	exists, err := container.DoesExist(ctx, ftlContainerName)
+	exists, err := container.DoesExist(ctx, ftlContainerName, optional.Some(image))
 	if err != nil {
 		return "", err
 	}
@@ -320,7 +321,7 @@ func (s *serveCmd) setupDB(ctx context.Context) (string, error) {
 			return "", fmt.Errorf("failed to close listener: %w", err)
 		}
 
-		err = container.RunDB(ctx, ftlContainerName, s.DBPort)
+		err = container.RunDB(ctx, ftlContainerName, s.DBPort, image)
 		if err != nil {
 			return "", err
 		}
diff --git a/docker-compose.yml b/docker-compose.yml
index 0d84b9d0ce..0854ce9ac6 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,6 +1,6 @@
 services:
   db:
-    image: postgres
+    image: postgres:15.4
     command: postgres
     user: postgres
     # For local debugging
diff --git a/internal/container/container.go b/internal/container/container.go
index 1956cd4a69..4bb5a82da8 100644
--- a/internal/container/container.go
+++ b/internal/container/container.go
@@ -24,8 +24,9 @@ var dockerClient = once.Once(func(ctx context.Context) (*client.Client, error) {
 	return client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
 })
 
-func DoesExist(ctx context.Context, name string) (bool, error) {
+func DoesExist(ctx context.Context, name string, image optional.Option[string]) (bool, error) {
 	cli, err := dockerClient.Get(ctx)
+	logger := log.FromContext(ctx)
 	if err != nil {
 		return false, err
 	}
@@ -37,8 +38,20 @@ func DoesExist(ctx context.Context, name string) (bool, error) {
 	if err != nil {
 		return false, fmt.Errorf("failed to list containers: %w", err)
 	}
-
-	return len(containers) > 0, nil
+	if len(containers) == 0 {
+		return false, nil
+	}
+	imageName, ok := image.Get()
+	if !ok {
+		return true, nil
+	}
+	for _, c := range containers {
+		if c.Image != imageName {
+			logger.Infof("possible database version mismatch, expecting to use container image %s for container with name %s, bit it was already running with image %s", image, name, c.Image)
+			break
+		}
+	}
+	return true, nil
 }
 
 // Pull pulls the given image.
@@ -105,28 +118,26 @@ func Run(ctx context.Context, image, name string, hostPort, containerPort int, v
 }
 
 // RunDB runs a new detached postgres container with the given name and exposed port.
-func RunDB(ctx context.Context, name string, port int) error {
+func RunDB(ctx context.Context, name string, port int, image string) error {
 	cli, err := dockerClient.Get(ctx)
 	if err != nil {
 		return err
 	}
 
-	const containerName = "postgres"
-
-	exists, err := DoesExist(ctx, containerName)
+	exists, err := DoesExist(ctx, name, optional.Some(image))
 	if err != nil {
 		return err
 	}
 
 	if !exists {
-		err = Pull(ctx, "postgres:latest")
+		err = Pull(ctx, image)
 		if err != nil {
 			return err
 		}
 	}
 
 	config := container.Config{
-		Image: "postgres:latest",
+		Image: image,
 		Env:   []string{"POSTGRES_PASSWORD=secret"},
 		User:  "postgres",
 		Cmd:   []string{"postgres"},