diff --git a/src/app/Backends/IMAP.php b/src/app/Backends/IMAP.php
--- a/src/app/Backends/IMAP.php
+++ b/src/app/Backends/IMAP.php
@@ -30,6 +30,19 @@
         return count($folders) > 0;
     }
 
+    /**
+     * Check if we can connect to the imap server
+     *
+     * @return bool True on success
+     */
+    public static function healthcheck(): bool
+    {
+        $config = self::getConfig();
+        $imap = self::initIMAP($config);
+        $imap->closeConnection();
+        return true;
+    }
+
     /**
      * Check if a shared folder is set up.
      *
diff --git a/src/app/Backends/LDAP.php b/src/app/Backends/LDAP.php
--- a/src/app/Backends/LDAP.php
+++ b/src/app/Backends/LDAP.php
@@ -64,6 +64,30 @@
         }
     }
 
+    /**
+     * Validates that ldap is available as configured.
+     *
+     * @throws \Exception
+     */
+    public static function healthcheck(): void
+    {
+        $config = self::getConfig('admin');
+        $ldap = self::initLDAP($config);
+
+        $mgmtRootDN = \config('ldap.admin.root_dn');
+        $hostedRootDN = \config('ldap.hosted.root_dn');
+
+        $result = $ldap->search($mgmtRootDN, '', 'base');
+        if (!$result || $result->count() != 1) {
+            self::throwException($ldap, "Failed to find the configured management domain $mgmtRootDN");
+        }
+
+        $result = $ldap->search($hostedRootDN, '', 'base');
+        if (!$result || $result->count() != 1) {
+            self::throwException($ldap, "Failed to find the configured hosted domain $hostedRootDN");
+        }
+    }
+
     /**
      * Create a domain in LDAP.
      *
diff --git a/src/app/Backends/OpenExchangeRates.php b/src/app/Backends/OpenExchangeRates.php
--- a/src/app/Backends/OpenExchangeRates.php
+++ b/src/app/Backends/OpenExchangeRates.php
@@ -49,4 +49,25 @@
 
         throw new \Exception("Failed to retrieve exchange rates");
     }
+
+    /**
+     * Validates that openexchange is available as configured.
+     *
+     * @throws \Exception
+     */
+    public static function healthcheck(): void
+    {
+        $apiKey = \config('services.openexchangerates.api_key');
+        if (!empty($apiKey)) {
+            $query = http_build_query(['app_id' => $apiKey]);
+            $url = 'https://openexchangerates.org/api/usage.json' . $query;
+            $html = file_get_contents($url, false);
+
+            if ($html && ($result = json_decode($html, true)) && !empty($result['status'])) {
+                print($result);
+            }
+
+            throw new \Exception("Failed to retrieve exchange rates status");
+        }
+    }
 }
diff --git a/src/app/Console/Commands/Status/Health.php b/src/app/Console/Commands/Status/Health.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/Status/Health.php
@@ -0,0 +1,207 @@
+<?php
+
+namespace App\Console\Commands\Status;
+
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Redis;
+use App\Backends\LDAP;
+use App\Backends\IMAP;
+use App\Backends\Roundcube;
+use App\Backends\OpenExchangeRates;
+use App\Providers\Payment\Mollie;
+
+//TODO stripe
+//TODO firebase
+
+class Health extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'status:health';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Check health of backends';
+
+    private function checkDB()
+    {
+        try {
+            $result = DB::select("SELECT 1");
+            return true;
+        } catch (\Exception $exception) {
+            $this->line($exception);
+            return false;
+        }
+    }
+
+    private function checkOpenExchangeRates()
+    {
+        try {
+            OpenExchangeRates::healthcheck();
+            return true;
+        } catch (\Exception $exception) {
+            $this->line($exception);
+            return false;
+        }
+    }
+
+    private function checkMollie()
+    {
+        try {
+            return Mollie::healthcheck();
+        } catch (\Exception $exception) {
+            $this->line($exception);
+            return false;
+        }
+    }
+
+    private function checkLDAP()
+    {
+        try {
+            LDAP::healthcheck();
+            return true;
+        } catch (\Exception $exception) {
+            $this->line($exception);
+            return false;
+        }
+    }
+
+    private function checkIMAP()
+    {
+        try {
+            IMAP::healthcheck();
+            return true;
+        } catch (\Exception $exception) {
+            $this->line($exception);
+            return false;
+        }
+    }
+
+    private function checkRoundcube()
+    {
+        try {
+            //TODO maybe run a select?
+            Roundcube::dbh();
+            return true;
+        } catch (\Exception $exception) {
+            $this->line($exception);
+            return false;
+        }
+    }
+
+    private function checkRedis()
+    {
+        try {
+            Redis::connection();
+            return true;
+        } catch (\Exception $exception) {
+            $this->line($exception);
+            return false;
+        }
+    }
+
+    private function checkMeet()
+    {
+        try {
+            $urls = \config('meet.api_urls');
+            foreach ($urls as $url) {
+                $this->line("Checking $url");
+
+                $client = new \GuzzleHttp\Client(
+                    [
+                        'http_errors' => false, // No exceptions from Guzzle
+                        'base_uri' => $url,
+                        'verify' => \config('meet.api_verify_tls'),
+                        'headers' => [
+                            'X-Auth-Token' => \config('meet.api_token'),
+                        ],
+                        'connect_timeout' => 10,
+                        'timeout' => 10,
+                        'on_stats' => function (\GuzzleHttp\TransferStats $stats) {
+                            $threshold = \config('logging.slow_log');
+                            if ($threshold && ($sec = $stats->getTransferTime()) > $threshold) {
+                                $url = $stats->getEffectiveUri();
+                                $method = $stats->getRequest()->getMethod();
+                                \Log::warning(sprintf("[STATS] %s %s: %.4f sec.", $method, $url, $sec));
+                            }
+                        },
+                    ]
+                );
+
+                $response = $client->request('GET', "ping");
+                if ($response->getStatusCode() != 200) {
+                    $this->line("Backend not available: " . var_export($response, true));
+                    return false;
+                }
+            }
+            return true;
+        } catch (\Exception $exception) {
+            $this->line($exception);
+            return false;
+        }
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $this->line("Checking DB...");
+        if ($this->checkDB()) {
+            $this->info("OK");
+        } else {
+            $this->error("Not found");
+        }
+        $this->line("Checking Redis...");
+        if ($this->checkRedis()) {
+            $this->info("OK");
+        } else {
+            $this->error("Not found");
+        }
+        $this->line("Checking LDAP...");
+        if ($this->checkLDAP()) {
+            $this->info("OK");
+        } else {
+            $this->error("Not found");
+        }
+        $this->line("Checking IMAP...");
+        if ($this->checkIMAP()) {
+            $this->info("OK");
+        } else {
+            $this->error("Not found");
+        }
+        $this->line("Checking Roundcube...");
+        if ($this->checkRoundcube()) {
+            $this->info("OK");
+        } else {
+            $this->error("Not found");
+        }
+        $this->line("Checking Meet...");
+        if ($this->checkMeet()) {
+            $this->info("OK");
+        } else {
+            $this->error("Not found");
+        }
+        $this->line("Checking OpenExchangeRates...");
+        if ($this->checkOpenExchangeRates()) {
+            $this->info("OK");
+        } else {
+            $this->error("Not found");
+        }
+        $this->line("Checking Mollie...");
+        if ($this->checkMollie()) {
+            $this->info("OK");
+        } else {
+            $this->error("Not found");
+        }
+    }
+}
diff --git a/src/app/Providers/Payment/Mollie.php b/src/app/Providers/Payment/Mollie.php
--- a/src/app/Providers/Payment/Mollie.php
+++ b/src/app/Providers/Payment/Mollie.php
@@ -33,6 +33,18 @@
         );
     }
 
+    /**
+    * Validates that mollie available.
+    *
+    * @throws \Mollie\Api\Exceptions\ApiException on failure
+    * @return bool true on success
+    */
+    public static function healthcheck()
+    {
+        mollie()->methods()->allActive();
+        return true;
+    }
+
     /**
      * Create a new auto-payment mandate for a wallet.
      *