diff --git a/config/cachet.php b/config/cachet.php
index 02b8696f..ea368dfd 100644
--- a/config/cachet.php
+++ b/config/cachet.php
@@ -148,4 +148,16 @@
         'zh_TW' => '繁體中文',
         'ph' => 'Filipino',
     ],
+
+    /*
+     |--------------------------------------------------------------------------
+     | Cachet Demo Mode
+     |--------------------------------------------------------------------------
+     |
+     | Whether to run Cachet in demo mode. This will adjust some of the default
+     | settings to allow Cachet to run in a demo environment.
+     |
+     */
+    'demo_mode' => env('CACHET_DEMO_MODE', false),
+
 ];
diff --git a/routes/console.php b/routes/console.php
new file mode 100644
index 00000000..42158b08
--- /dev/null
+++ b/routes/console.php
@@ -0,0 +1,16 @@
+<?php
+
+use Cachet\Cachet;
+use Cachet\Database\Seeders\DatabaseSeeder;
+use Illuminate\Support\Facades\Schedule;
+
+$demoMode = fn () => Cachet::demoMode();
+
+// Allows Cachet to be hosted in limited deployment environments,
+// where we're unable to create custom scheduled jobs.
+Schedule::command('db:seed', [
+    '--class' => DatabaseSeeder::class,
+    '--force' => true,
+])
+    ->everyThirtyMinutes()
+    ->when($demoMode);
diff --git a/src/Cachet.php b/src/Cachet.php
index 206fc8e7..f1d2144b 100644
--- a/src/Cachet.php
+++ b/src/Cachet.php
@@ -94,4 +94,12 @@ public static function getResourceApiAbilities(): array
             'schedule-updates' => ['manage', 'delete'],
         ];
     }
+
+    /**
+     * Determine if Cachet is running in demo mode.
+     */
+    public static function demoMode(): bool
+    {
+        return (bool) config('cachet.demo_mode');
+    }
 }
diff --git a/src/CachetCoreServiceProvider.php b/src/CachetCoreServiceProvider.php
index d8dd5da0..2cbf34f5 100644
--- a/src/CachetCoreServiceProvider.php
+++ b/src/CachetCoreServiceProvider.php
@@ -168,6 +168,8 @@ private function registerBladeComponents(): void
     private function registerCommands(): void
     {
         if ($this->app->runningInConsole()) {
+            $this->loadRoutesFrom(__DIR__.'/../routes/console.php');
+
             $this->commands([
                 Commands\MakeUserCommand::class,
                 Commands\SendBeaconCommand::class,
diff --git a/src/Filament/Resources/UserResource.php b/src/Filament/Resources/UserResource.php
index 12a04728..e6913ded 100644
--- a/src/Filament/Resources/UserResource.php
+++ b/src/Filament/Resources/UserResource.php
@@ -11,6 +11,7 @@
 use Filament\Tables;
 use Filament\Tables\Table;
 use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Model;
 use Illuminate\Support\Facades\Hash;
 
 class UserResource extends Resource
@@ -22,6 +23,11 @@ public static function canAccess(): bool
         return auth()->user()->isAdmin();
     }
 
+    public static function canEdit(Model $record): bool
+    {
+        return ! Cachet::demoMode() && (auth()->user()->is($record) || auth()->user()->isAdmin());
+    }
+
     public static function form(Form $form): Form
     {
         return $form
diff --git a/tests/Unit/Commands/ConsoleTest.php b/tests/Unit/Commands/ConsoleTest.php
new file mode 100644
index 00000000..2cb792aa
--- /dev/null
+++ b/tests/Unit/Commands/ConsoleTest.php
@@ -0,0 +1,21 @@
+<?php
+
+use Cachet\Database\Seeders\DatabaseSeeder;
+use Illuminate\Console\Scheduling\Schedule;
+
+it('registers a scheduled job', function () {
+    // Resolve the schedule instance from the application container
+    $schedule = app(Schedule::class);
+
+    $events = collect($schedule->events())->keyBy('command')->keys()->all();
+
+    // Build the expected scheduled command
+    $scheduledCommand = sprintf("'%s' 'artisan' db:seed --class='%s' --force='1'",
+        PHP_BINARY,
+        DatabaseSeeder::class,
+    );
+
+    expect($events)
+        ->toContain($scheduledCommand);
+});
+