diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml
index 5606bb2c977221eb07ed150c30eff1b6736ad8a6..fd6cfd7841c056d96d2e523c96f6643742e4272a 100644
--- a/build/psalm-baseline.xml
+++ b/build/psalm-baseline.xml
@@ -5098,8 +5098,9 @@
     </UndefinedInterfaceMethod>
   </file>
   <file src="lib/private/Support/Subscription/Registry.php">
-    <UndefinedInterfaceMethod occurrences="1">
+    <UndefinedInterfaceMethod occurrences="2">
       <code>getSupportedApps</code>
+      <code>countUsers</code>
     </UndefinedInterfaceMethod>
   </file>
   <file src="lib/private/SystemTag/SystemTagManager.php">
diff --git a/lib/private/Support/Subscription/Registry.php b/lib/private/Support/Subscription/Registry.php
index 3d6a9b09d84250af391817f0df8acd551f78f36d..ba9c4099b9bdecc240046b33785178306f5fab11 100644
--- a/lib/private/Support/Subscription/Registry.php
+++ b/lib/private/Support/Subscription/Registry.php
@@ -28,13 +28,16 @@ declare(strict_types=1);
 
 namespace OC\Support\Subscription;
 
+use OC\User\Backend;
 use OCP\AppFramework\QueryException;
 use OCP\IConfig;
 use OCP\IServerContainer;
+use OCP\IUserManager;
 use OCP\Support\Subscription\Exception\AlreadyRegisteredException;
 use OCP\Support\Subscription\IRegistry;
 use OCP\Support\Subscription\ISubscription;
 use OCP\Support\Subscription\ISupportedApps;
+use Psr\Log\LoggerInterface;
 
 class Registry implements IRegistry {
 
@@ -49,10 +52,19 @@ class Registry implements IRegistry {
 
 	/** @var IServerContainer */
 	private $container;
-
-	public function __construct(IConfig $config, IServerContainer $container) {
+	/** @var IUserManager */
+	private $userManager;
+	/** @var LoggerInterface */
+	private $logger;
+
+	public function __construct(IConfig $config,
+								IServerContainer $container,
+								IUserManager $userManager,
+								LoggerInterface $logger) {
 		$this->config = $config;
 		$this->container = $container;
+		$this->userManager = $userManager;
+		$this->logger = $logger;
 	}
 
 	private function getSubscription(): ?ISubscription {
@@ -127,9 +139,76 @@ class Registry implements IRegistry {
 	 * @since 17.0.0
 	 */
 	public function delegateHasExtendedSupport(): bool {
-		if ($this->getSubscription() instanceof ISubscription && method_exists($this->subscription, 'hasExtendedSupport')) {
+		if ($this->getSubscription() instanceof ISubscription) {
 			return $this->getSubscription()->hasExtendedSupport();
 		}
 		return false;
 	}
+
+
+	/**
+	 * Indicates if a hard user limit is reached and no new users should be created
+	 *
+	 * @since 21.0.0
+	 */
+	public function delegateIsHardUserLimitReached(): bool {
+		$subscription = $this->getSubscription();
+		if ($subscription instanceof ISubscription &&
+			$subscription->hasValidSubscription()) {
+			$userLimitReached = $subscription->isHardUserLimitReached();
+			if ($userLimitReached) {
+				$this->notifyAboutReachedUserLimit();
+			}
+			return $userLimitReached;
+		}
+
+		$isOneClickInstance = $this->config->getSystemValueBool('one-click-instance', false);
+
+		if (!$isOneClickInstance) {
+			return false;
+		}
+
+		$userCount = $this->getUserCount();
+		$hardUserLimit = $this->config->getSystemValue('one-click-instance.user-limit', 50);
+
+		$userLimitReached = $userCount >= $hardUserLimit;
+		if ($userLimitReached) {
+			$this->notifyAboutReachedUserLimit();
+		}
+		return $userLimitReached;
+	}
+
+	private function getUserCount(): int {
+		$userCount = 0;
+		$backends = $this->userManager->getBackends();
+		foreach ($backends as $backend) {
+			if ($backend->implementsActions(Backend::COUNT_USERS)) {
+				$backendUsers = $backend->countUsers();
+				if ($backendUsers !== false) {
+					$userCount += $backendUsers;
+				} else {
+					// TODO what if the user count can't be determined?
+					$this->logger->warning('Can not determine user count for ' . get_class($backend), ['app' => 'lib']);
+				}
+			}
+		}
+
+		$disabledUsers = $this->config->getUsersForUserValue('core', 'enabled', 'false');
+		$disabledUsersCount = count($disabledUsers);
+		$userCount = $userCount - $disabledUsersCount;
+
+		if ($userCount < 0) {
+			$userCount = 0;
+
+			// this should never happen
+			$this->logger->warning("Total user count was negative (users: $userCount, disabled: $disabledUsersCount)", ['app' => 'lib']);
+		}
+
+		return $userCount;
+	}
+
+	private function notifyAboutReachedUserLimit() {
+		// TODO notify admin about reached user limit
+		$this->logger->warning('The user limit was reached and the new user was not created', ['app' => 'lib']);
+	}
 }
diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php
index 1201a456ce239352cdf57451b8bfb5aba766a372..1d58c68268c6b9647b0c1b31776db6942e6e3e43 100644
--- a/lib/private/User/Manager.php
+++ b/lib/private/User/Manager.php
@@ -34,6 +34,7 @@
 
 namespace OC\User;
 
+use OC\HintException;
 use OC\Hooks\PublicEmitter;
 use OCP\DB\QueryBuilder\IQueryBuilder;
 use OCP\EventDispatcher\IEventDispatcher;
@@ -42,6 +43,7 @@ use OCP\IGroup;
 use OCP\IUser;
 use OCP\IUserBackend;
 use OCP\IUserManager;
+use OCP\Support\Subscription\IRegistry;
 use OCP\User\Backend\IGetRealUIDBackend;
 use OCP\User\Events\BeforeUserCreatedEvent;
 use OCP\User\Events\UserCreatedEvent;
@@ -297,6 +299,12 @@ class Manager extends PublicEmitter implements IUserManager {
 	 * @return bool|IUser the created user or false
 	 */
 	public function createUser($uid, $password) {
+		// DI injection is not used here as IRegistry needs the user manager itself for user count and thus it would create a cyclic dependency
+		if (\OC::$server->get(IRegistry::class)->delegateIsHardUserLimitReached()) {
+			$l = \OC::$server->getL10N('lib');
+			throw new HintException($l->t('The user limit has been reached and the user was not created.'));
+		}
+
 		$localBackends = [];
 		foreach ($this->backends as $backend) {
 			if ($backend instanceof Database) {
diff --git a/lib/public/Support/Subscription/IRegistry.php b/lib/public/Support/Subscription/IRegistry.php
index f3d6cf2f92ca21e03ee7db462945f30d05301bb8..ab1d00fa82307deb75e1cb9661c73064a5d6308a 100644
--- a/lib/public/Support/Subscription/IRegistry.php
+++ b/lib/public/Support/Subscription/IRegistry.php
@@ -78,4 +78,11 @@ interface IRegistry {
 	 * @since 17.0.0
 	 */
 	public function delegateHasExtendedSupport(): bool;
+
+	/**
+	 * Indicates if a hard user limit is reached and no new users should be created
+	 *
+	 * @since 21.0.0
+	 */
+	public function delegateIsHardUserLimitReached(): bool;
 }
diff --git a/lib/public/Support/Subscription/ISubscription.php b/lib/public/Support/Subscription/ISubscription.php
index 83b7509b9fefa47e3cf605a6acf4260f11039f6e..9614c0ed77b3075a94e8b44ab37bb8dbf85192f4 100644
--- a/lib/public/Support/Subscription/ISubscription.php
+++ b/lib/public/Support/Subscription/ISubscription.php
@@ -45,4 +45,11 @@ interface ISubscription {
 	 * @since 17.0.0
 	 */
 	public function hasExtendedSupport(): bool;
+
+	/**
+	 * Indicates if a hard user limit is reached and no new users should be created
+	 *
+	 * @since 21.0.0
+	 */
+	public function isHardUserLimitReached(): bool;
 }
diff --git a/tests/lib/Support/Subscription/DummySubscription.php b/tests/lib/Support/Subscription/DummySubscription.php
index e1f7f5c6b61adf242acf29e22cd96c0955898ef3..f4e7e3484bb557e1b5b2dd31c13b2386b5f04b17 100644
--- a/tests/lib/Support/Subscription/DummySubscription.php
+++ b/tests/lib/Support/Subscription/DummySubscription.php
@@ -30,6 +30,8 @@ class DummySubscription implements \OCP\Support\Subscription\ISubscription {
 	private $hasValidSubscription;
 	/** @var bool */
 	private $hasExtendedSupport;
+	/** @var bool */
+	private $isHardUserLimitReached;
 
 	/**
 	 * DummySubscription constructor.
@@ -37,9 +39,10 @@ class DummySubscription implements \OCP\Support\Subscription\ISubscription {
 	 * @param bool $hasValidSubscription
 	 * @param bool $hasExtendedSupport
 	 */
-	public function __construct(bool $hasValidSubscription, bool $hasExtendedSupport) {
+	public function __construct(bool $hasValidSubscription, bool $hasExtendedSupport, bool $isHardUserLimitReached) {
 		$this->hasValidSubscription = $hasValidSubscription;
 		$this->hasExtendedSupport = $hasExtendedSupport;
+		$this->isHardUserLimitReached = $isHardUserLimitReached;
 	}
 
 	/**
@@ -55,4 +58,8 @@ class DummySubscription implements \OCP\Support\Subscription\ISubscription {
 	public function hasExtendedSupport(): bool {
 		return $this->hasExtendedSupport;
 	}
+
+	public function isHardUserLimitReached(): bool {
+		return $this->isHardUserLimitReached;
+	}
 }
diff --git a/tests/lib/Support/Subscription/RegistryTest.php b/tests/lib/Support/Subscription/RegistryTest.php
index c070f69ae66c7e71647e884ac4ed6b9a1090fb1c..58995afcab81011c7016a059f3041358f4a0c940 100644
--- a/tests/lib/Support/Subscription/RegistryTest.php
+++ b/tests/lib/Support/Subscription/RegistryTest.php
@@ -25,9 +25,11 @@ namespace Test\Support\Subscription;
 use OC\Support\Subscription\Registry;
 use OCP\IConfig;
 use OCP\IServerContainer;
+use OCP\IUserManager;
 use OCP\Support\Subscription\ISubscription;
 use OCP\Support\Subscription\ISupportedApps;
 use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
 use Test\TestCase;
 
 class RegistryTest extends TestCase {
@@ -41,12 +43,25 @@ class RegistryTest extends TestCase {
 	/** @var MockObject|IServerContainer */
 	private $serverContainer;
 
+	/** @var MockObject|IUserManager */
+	private $userManager;
+
+	/** @var MockObject|LoggerInterface */
+	private $logger;
+
 	protected function setUp(): void {
 		parent::setUp();
 
 		$this->config = $this->createMock(IConfig::class);
 		$this->serverContainer = $this->createMock(IServerContainer::class);
-		$this->registry = new Registry($this->config, $this->serverContainer);
+		$this->userManager = $this->createMock(IUserManager::class);
+		$this->logger = $this->createMock(LoggerInterface::class);
+		$this->registry = new Registry(
+			$this->config,
+			$this->serverContainer,
+			$this->userManager,
+			$this->logger
+		);
 	}
 
 	/**
@@ -121,10 +136,74 @@ class RegistryTest extends TestCase {
 	public function testSubscriptionService() {
 		$this->serverContainer->method('query')
 			->with(DummySubscription::class)
-			->willReturn(new DummySubscription(true, false));
+			->willReturn(new DummySubscription(true, false, false));
 		$this->registry->registerService(DummySubscription::class);
 
 		$this->assertTrue($this->registry->delegateHasValidSubscription());
 		$this->assertFalse($this->registry->delegateHasExtendedSupport());
 	}
+
+	public function testDelegateIsHardUserLimitReached() {
+		/* @var ISubscription|\PHPUnit\Framework\MockObject\MockObject $subscription */
+		$subscription = $this->createMock(ISubscription::class);
+		$subscription->expects($this->once())
+			->method('hasValidSubscription')
+			->willReturn(true);
+		$subscription->expects($this->once())
+			->method('isHardUserLimitReached')
+			->willReturn(true);
+		$this->registry->register($subscription);
+
+		$this->assertSame(true, $this->registry->delegateIsHardUserLimitReached());
+	}
+
+	public function testDelegateIsHardUserLimitReachedWithoutSupportApp() {
+		$this->config->expects($this->once())
+			->method('getSystemValueBool')
+			->with('one-click-instance')
+			->willReturn(false);
+
+		$this->assertSame(false, $this->registry->delegateIsHardUserLimitReached());
+	}
+
+	public function dataForUserLimitCheck() {
+		return [
+			// $userLimit, $userCount, $disabledUsers, $expectedResult
+			[35, 15, 2, false],
+			[35, 45, 15, false],
+			[35, 45, 5, true],
+			[35, 45, 55, false],
+		];
+	}
+
+	/**
+	 * @dataProvider dataForUserLimitCheck
+	 */
+	public function testDelegateIsHardUserLimitReachedWithoutSupportAppAndUserCount($userLimit, $userCount, $disabledUsers, $expectedResult) {
+		$this->config->expects($this->once())
+			->method('getSystemValueBool')
+			->with('one-click-instance')
+			->willReturn(true);
+		$this->config->expects($this->once())
+			->method('getSystemValue')
+			->with('one-click-instance.user-limit')
+			->willReturn($userLimit);
+		$this->config->expects($this->once())
+			->method('getUsersForUserValue')
+			->with('core', 'enabled', 'false')
+			->willReturn(array_fill(0, $disabledUsers, ''));
+		/* @var UserInterface|\PHPUnit\Framework\MockObject\MockObject $dummyBackend */
+		$dummyBackend = $this->createMock(UserInterface::class);
+		$dummyBackend->expects($this->once())
+			->method('implementsActions')
+			->willReturn(true);
+		$dummyBackend->expects($this->once())
+			->method('countUsers')
+			->willReturn($userCount);
+		$this->userManager->expects($this->once())
+			->method('getBackends')
+			->willReturn([$dummyBackend]);
+
+		$this->assertSame($expectedResult, $this->registry->delegateIsHardUserLimitReached());
+	}
 }