diff --git a/CHANGELOG.md b/CHANGELOG.md
index 07974e9bbe0ad1341a22cf95961bd9126615678e..8623aca7d2665657079b2f47395196e9492090d1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 * Added support for `image/jxl` thumbnailing.
 * Built-in early support for content ranges (being able to skip around in audio and video). This is only available if
   caching is enabled.
+* New config option for changing the log level.
 
 ### Removed
 
diff --git a/cmd/export_synapse_for_import/main.go b/cmd/export_synapse_for_import/main.go
index 84a86203909150498700dc9bf8df06e893144b54..1a9cb49f6675b63777002411458e907b24914942 100644
--- a/cmd/export_synapse_for_import/main.go
+++ b/cmd/export_synapse_for_import/main.go
@@ -58,7 +58,12 @@ func main() {
 		realPsqlPassword = *postgresPassword
 	}
 
-	err := logging.Setup(config.Get().General.LogDirectory, config.Get().General.LogColors, config.Get().General.JsonLogs)
+	err := logging.Setup(
+		config.Get().General.LogDirectory,
+		config.Get().General.LogColors,
+		config.Get().General.JsonLogs,
+		config.Get().General.LogLevel,
+	)
 	if err != nil {
 		panic(err)
 	}
diff --git a/cmd/gdpr_export/main.go b/cmd/gdpr_export/main.go
index 06d3122225582d3b1bf08e7db1e533bbb6254403..4b0e4bfad049caf72a8d0fa7785abfeeead17af1 100644
--- a/cmd/gdpr_export/main.go
+++ b/cmd/gdpr_export/main.go
@@ -45,7 +45,12 @@ func main() {
 	assets.SetupTemplates(*templatesPath)
 
 	var err error
-	err = logging.Setup(config.Get().General.LogDirectory, config.Get().General.LogColors, config.Get().General.JsonLogs)
+	err = logging.Setup(
+		config.Get().General.LogDirectory,
+		config.Get().General.LogColors,
+		config.Get().General.JsonLogs,
+		config.Get().General.LogLevel,
+	)
 	if err != nil {
 		panic(err)
 	}
@@ -83,7 +88,7 @@ func main() {
 			if err != nil {
 				logrus.Error(err)
 			} else if task.EndTs > 0 {
-				waitChan<-true
+				waitChan <- true
 				return
 			}
 
diff --git a/cmd/gdpr_import/main.go b/cmd/gdpr_import/main.go
index c69035a011e777c48255984497276c395690c134..d26852511bd0a03f7112e0e46559a7ac78894427 100644
--- a/cmd/gdpr_import/main.go
+++ b/cmd/gdpr_import/main.go
@@ -35,7 +35,12 @@ func main() {
 	assets.SetupMigrations(*migrationsPath)
 
 	var err error
-	err = logging.Setup(config.Get().General.LogDirectory, config.Get().General.LogColors, config.Get().General.JsonLogs)
+	err = logging.Setup(
+		config.Get().General.LogDirectory,
+		config.Get().General.LogColors,
+		config.Get().General.JsonLogs,
+		config.Get().General.LogLevel,
+	)
 	if err != nil {
 		panic(err)
 	}
@@ -155,7 +160,7 @@ func main() {
 			if err != nil {
 				logrus.Error(err)
 			} else if task.EndTs > 0 {
-				waitChan<-true
+				waitChan <- true
 				return
 			}
 
diff --git a/cmd/import_synapse/main.go b/cmd/import_synapse/main.go
index db184c78ee78b4ab373dcd93f7921d5fb2fbf055..3c97b89c54404beae545d8c6a6f986fbeedd7175 100644
--- a/cmd/import_synapse/main.go
+++ b/cmd/import_synapse/main.go
@@ -72,7 +72,12 @@ func main() {
 		realPsqlPassword = *postgresPassword
 	}
 
-	err := logging.Setup(config.Get().General.LogDirectory, config.Get().General.LogColors, config.Get().General.JsonLogs)
+	err := logging.Setup(
+		config.Get().General.LogDirectory,
+		config.Get().General.LogColors,
+		config.Get().General.JsonLogs,
+		config.Get().General.LogLevel,
+	)
 	if err != nil {
 		panic(err)
 	}
diff --git a/cmd/media_repo/main.go b/cmd/media_repo/main.go
index 061799929339e990baa86f84fadd559999b7de28..ba828cb814e150e39583be0a5b5503ecd4a62939 100644
--- a/cmd/media_repo/main.go
+++ b/cmd/media_repo/main.go
@@ -59,7 +59,12 @@ func main() {
 	assets.SetupTemplates(*templatesPath)
 	assets.SetupAssets(*assetsPath)
 
-	err := logging.Setup(config.Get().General.LogDirectory, config.Get().General.LogColors, config.Get().General.JsonLogs)
+	err := logging.Setup(
+		config.Get().General.LogDirectory,
+		config.Get().General.LogColors,
+		config.Get().General.JsonLogs,
+		config.Get().General.LogLevel,
+	)
 	if err != nil {
 		panic(err)
 	}
diff --git a/common/config/conf_main.go b/common/config/conf_main.go
index 7e96d8c39b13430c8a3b09b211b8f57549caec6f..ecc508d093b6ba79d36f8e1a95011ae18e541d90 100644
--- a/common/config/conf_main.go
+++ b/common/config/conf_main.go
@@ -27,6 +27,7 @@ func NewDefaultMainConfig() MainRepoConfig {
 			LogDirectory:     "logs",
 			LogColors:        false,
 			JsonLogs:         false,
+			LogLevel:         "info",
 			TrustAnyForward:  false,
 			UseForwardedHost: true,
 		},
diff --git a/common/config/models_main.go b/common/config/models_main.go
index fc5fb516f9ecb0d38c3f4b9a79f13c4b595a2c8e..084ff06cf2af6c005f35231b6727316b80993031 100644
--- a/common/config/models_main.go
+++ b/common/config/models_main.go
@@ -6,6 +6,7 @@ type GeneralConfig struct {
 	LogDirectory     string `yaml:"logDirectory"`
 	LogColors        bool   `yaml:"logColors"`
 	JsonLogs         bool   `yaml:"jsonLogs"`
+	LogLevel         string `yaml:"logLevel"`
 	TrustAnyForward  bool   `yaml:"trustAnyForwardedAddress"`
 	UseForwardedHost bool   `yaml:"useForwardedHost"`
 }
diff --git a/common/logging/logger.go b/common/logging/logger.go
index 86cba36396cbf58722ad066540e61697dd621d2a..e3531cd491dc30e98cd0f5f1d035b0fb5b665e6c 100644
--- a/common/logging/logger.go
+++ b/common/logging/logger.go
@@ -19,7 +19,16 @@ func (f utcFormatter) Format(entry *logrus.Entry) ([]byte, error) {
 	return f.Formatter.Format(entry)
 }
 
-func Setup(dir string, colors bool, json bool) error {
+func Setup(dir string, colors bool, json bool, level string) error {
+	if level == "" {
+		level = "info"
+	}
+	lvl, err := logrus.ParseLevel(level)
+	if err != nil {
+		return err
+	}
+	logrus.SetLevel(lvl)
+
 	var lineFormatter logrus.Formatter
 	if json {
 		lineFormatter = &logrus.JSONFormatter{
diff --git a/config.sample.yaml b/config.sample.yaml
index 670aaf845fa7e690c6f68415897066ea44d6e593..fa49e74f7e49e2c8678d85d78cee320d7c0d2314 100644
--- a/config.sample.yaml
+++ b/config.sample.yaml
@@ -19,6 +19,11 @@ repo:
   # incompatible with the log color option and will always render without colors.
   jsonLogs: false
 
+  # The log level to log at. Note that this will need to be at least "info" to receive support.
+  #
+  # Values (in increasing spam): panic | fatal | error | warn | info | debug | trace
+  logLevel: "info"
+
   # If true, the media repo will accept any X-Forwarded-For header without validation. In most cases
   # this option should be left as "false". Note that the media repo already expects an X-Forwarded-For
   # header, but validates it to ensure the IP being given makes sense.