diff --git a/app/Livewire/Boarding/Index.php b/app/Livewire/Boarding/Index.php index 430470fa0..e6f4c1d7b 100644 --- a/app/Livewire/Boarding/Index.php +++ b/app/Livewire/Boarding/Index.php @@ -16,14 +16,18 @@ class Index extends Component { protected $listeners = ['refreshBoardingIndex' => 'validateServer']; + #[\Livewire\Attributes\Url(as: 'step', history: true)] public string $currentState = 'welcome'; + #[\Livewire\Attributes\Url(keep: true)] public ?string $selectedServerType = null; public ?Collection $privateKeys = null; + #[\Livewire\Attributes\Url(keep: true)] public ?int $selectedExistingPrivateKey = null; + #[\Livewire\Attributes\Url(keep: true)] public ?string $privateKeyType = null; public ?string $privateKey = null; @@ -38,6 +42,7 @@ class Index extends Component public ?Collection $servers = null; + #[\Livewire\Attributes\Url(keep: true)] public ?int $selectedExistingServer = null; public ?string $remoteServerName = null; @@ -58,6 +63,7 @@ class Index extends Component public Collection $projects; + #[\Livewire\Attributes\Url(keep: true)] public ?int $selectedProject = null; public ?Project $createdProject = null; @@ -79,17 +85,63 @@ public function mount() $this->minDockerVersion = str(config('constants.docker.minimum_required_version'))->before('.'); $this->privateKeyName = generate_random_name(); $this->remoteServerName = generate_random_name(); - if (isDev()) { - $this->privateKey = '-----BEGIN OPENSSH PRIVATE KEY----- -b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW -QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk -hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA -AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV -uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== ------END OPENSSH PRIVATE KEY-----'; - $this->privateKeyDescription = 'Created by Coolify'; - $this->remoteServerDescription = 'Created by Coolify'; - $this->remoteServerHost = 'coolify-testing-host'; + + // Initialize collections to avoid null errors + if ($this->privateKeys === null) { + $this->privateKeys = collect(); + } + if ($this->servers === null) { + $this->servers = collect(); + } + if (! isset($this->projects)) { + $this->projects = collect(); + } + + // Restore state when coming from URL with query params + if ($this->selectedServerType === 'localhost' && $this->selectedExistingServer === 0) { + $this->createdServer = Server::find(0); + if ($this->createdServer) { + $this->serverPublicKey = $this->createdServer->privateKey->getPublicKey(); + } + } + + if ($this->selectedServerType === 'remote') { + if ($this->privateKeys->isEmpty()) { + $this->privateKeys = PrivateKey::ownedByCurrentTeam(['name'])->where('id', '!=', 0)->get(); + } + if ($this->servers->isEmpty()) { + $this->servers = Server::ownedByCurrentTeam(['name'])->where('id', '!=', 0)->get(); + } + + if ($this->selectedExistingServer) { + $this->createdServer = Server::find($this->selectedExistingServer); + if ($this->createdServer) { + $this->serverPublicKey = $this->createdServer->privateKey->getPublicKey(); + $this->updateServerDetails(); + } + } + + if ($this->selectedExistingPrivateKey) { + $this->createdPrivateKey = PrivateKey::where('team_id', currentTeam()->id) + ->where('id', $this->selectedExistingPrivateKey) + ->first(); + if ($this->createdPrivateKey) { + $this->privateKey = $this->createdPrivateKey->private_key; + $this->publicKey = $this->createdPrivateKey->getPublicKey(); + } + } + + // Auto-regenerate key pair for "Generate with Coolify" mode on page refresh + if ($this->privateKeyType === 'create' && empty($this->privateKey)) { + $this->createNewPrivateKey(); + } + } + + if ($this->selectedProject) { + $this->createdProject = Project::find($this->selectedProject); + if (! $this->createdProject) { + $this->projects = Project::ownedByCurrentTeam(['name'])->get(); + } } } @@ -129,11 +181,7 @@ public function setServerType(string $type) return $this->validateServer('localhost'); } elseif ($this->selectedServerType === 'remote') { - if (isDev()) { - $this->privateKeys = PrivateKey::ownedByCurrentTeam(['name'])->get(); - } else { - $this->privateKeys = PrivateKey::ownedByCurrentTeam(['name'])->where('id', '!=', 0)->get(); - } + $this->privateKeys = PrivateKey::ownedByCurrentTeam(['name'])->where('id', '!=', 0)->get(); if ($this->privateKeys->count() > 0) { $this->selectedExistingPrivateKey = $this->privateKeys->first()->id; } @@ -202,6 +250,9 @@ public function setPrivateKey(string $type) $this->privateKeyType = $type; if ($type === 'create') { $this->createNewPrivateKey(); + } else { + $this->privateKey = null; + $this->publicKey = null; } $this->currentState = 'create-private-key'; } diff --git a/resources/views/components/boarding-progress.blade.php b/resources/views/components/boarding-progress.blade.php new file mode 100644 index 000000000..a946a7471 --- /dev/null +++ b/resources/views/components/boarding-progress.blade.php @@ -0,0 +1,47 @@ +@props(['currentStep' => 1, 'totalSteps' => 3]) + +
+
+ @for ($i = 1; $i <= $totalSteps; $i++) +
+
+
+ @if ($i < $currentStep) + + + + @else + + {{ $i }} + + @endif +
+ + @if ($i === 1) + Server + @elseif ($i === 2) + Connection + @elseif ($i === 3) + Complete + @endif + +
+ @if ($i < $totalSteps) +
+
+ @endif +
+ @endfor +
+
diff --git a/resources/views/components/boarding-step.blade.php b/resources/views/components/boarding-step.blade.php index d963e55f0..3e6dc92e5 100644 --- a/resources/views/components/boarding-step.blade.php +++ b/resources/views/components/boarding-step.blade.php @@ -1,25 +1,29 @@ -
-
-

{{ $title }}

-
+
+
+
+

{{ $title }}

@isset($question) -

+

{{ $question }} -

+
@endisset + + @if ($actions) +
+ {{ $actions }} +
+ @endif
- @if ($actions) -
- {{ $actions }} + + @isset($explanation) +
+

+ Technical Details +

+
+ {{ $explanation }} +
- @endif + @endisset
- @isset($explanation) -
-

Explanation

-
- {{ $explanation }} -
-
- @endisset
diff --git a/resources/views/layouts/boarding.blade.php b/resources/views/layouts/boarding.blade.php index 67c1cc998..e0b5f49a3 100644 --- a/resources/views/layouts/boarding.blade.php +++ b/resources/views/layouts/boarding.blade.php @@ -1,6 +1,6 @@ @extends('layouts.base') @section('body') -
+
{{ $slot }}
@parent diff --git a/resources/views/livewire/boarding/index.blade.php b/resources/views/livewire/boarding/index.blade.php index 68b328b30..e396611a8 100644 --- a/resources/views/livewire/boarding/index.blade.php +++ b/resources/views/livewire/boarding/index.blade.php @@ -2,68 +2,160 @@ Onboarding | Coolify -
-
+
+
@if ($currentState === 'welcome') -

Welcome to Coolify

-
Let me help you set up the basics.
-
- Get - Started - +
+
+

Welcome to Coolify

+

+ Connect your first server and start deploying in minutes +

+
+ +
+

+ What You'll Set Up +

+
+
+
+ + + +
+
+
Server Connection
+
Connect via SSH to deploy your resources
+
+
+
+
+ + + +
+
+
Docker Environment
+
Automated installation and configuration
+
+
+
+
+ + + +
+
+
Project Structure
+
Organize your applications and resources
+
+
+
+
+ +
+ + Start Setup + +
@elseif ($currentState === 'explanation') - + + - Coolify is an all-in-one application to automate tasks on your servers, deploy applications with - Git - integrations, deploy databases and services, monitor these resources with notifications and - alerts - without vendor lock-in.
- Coolify Home. -

- - + Coolify automates deployment and infrastructure management on your own servers. Deploy applications + from Git, manage databases, and monitor everything—without vendor lock-in.

- You don't need to manage your servers anymore. - Coolify does - it for you. + Coolify handles server configuration, Docker management, and + deployments automatically.

- All configurations are stored on your servers, so - everything works without a connection to Coolify (except integrations and automations). + All data and configurations live on your infrastructure. + Works offline except for external integrations.

- You can get notified on your favourite platforms - (Discord, - Telegram, Email, etc.) when something goes wrong, or an action is needed from your side. + Get real-time notifications via Discord, Telegram, + Email, and other platforms.

- Next + + Continue
@elseif ($currentState === 'select-server-type') - + + - Do you want to deploy your resources to your - - or to a - ? + Select where to deploy your applications and databases. You can add more servers later. - Localhost - +
+ - Remote Server - + +
@if (!$serverReachable)
@@ -111,51 +203,97 @@ class="bg-red-200 dark:bg-red-900 px-1 rounded-sm">~/.ssh/authorized_keys @endif -

Servers are the main building blocks, as they will host your applications, databases, - services, called resources. Any CPU intensive process will use the server's CPU where you - are deploying your resources.

- is the server where Coolify is running on. It is not - recommended to use one server - for everything. + host your applications, databases, and services (collectively + called resources). All CPU-intensive operations run on the target server.

- is a server reachable through SSH. It can be hosted - at home, or from any cloud - provider. + The machine running Coolify. Not recommended for production + workloads due to resource contention. +

+

+ Any SSH-accessible server—cloud providers (AWS, Hetzner, + DigitalOcean), bare metal, or self-hosted infrastructure.

@elseif ($currentState === 'private-key') - + + - Do you have your own SSH Private Key? + Configure SSH key-based authentication for secure server access. - Yes - - No (create one for me) - - @if (count($privateKeys) > 0) -
- - @foreach ($privateKeys as $privateKey) - - @endforeach - - Use this SSH Key -
+ @if ($privateKeys && $privateKeys->count() > 0) +
+
+
+ + @foreach ($privateKeys as $privateKey) + + @endforeach + + Use Selected Key +
+
+
+
+
+
+
+
+ OR +
+
+
+
@endif +
+ +
+ + + +
+

Use Existing Key

+

I have my own SSH key

+
+
+
+ +
+ + + +
+

Generate New Key

+

Create ED25519 key pair

+
+
+
+
-

SSH Keys are used to connect to a remote server through a secure shell, called SSH.

-

You can use your own ssh private key, or you can let Coolify to create one for you.

-

In both ways, you need to add the public version of your ssh private key to the remote - server's - ~/.ssh/authorized_keys file. +

+ Uses public-key cryptography for secure, + password-less server access. +

+

+ Add the public key to your server's + ~/.ssh/authorized_keys + file. +

+

+ Coolify generates ED25519 keys by default for optimal + security and performance.

@@ -237,164 +375,385 @@ class="bg-red-200 dark:bg-red-900 px-1 rounded-sm">~/.ssh/authorized_keys
@elseif ($currentState === 'create-private-key') - + + - Please let me know your key details. + Configure your SSH key for server authentication. -
- - + + - @if ($privateKeyType === 'create') + - ACTION REQUIRED: Copy the 'Public Key' to your - server's - ~/.ssh/authorized_keys - file. + @else + @endif - Save + @if ($privateKeyType === 'create') +
+
+ + + +
+

Action Required

+

+ Copy the public key above and add it to your server's + ~/.ssh/authorized_keys + file. +

+
+
+
+ @endif + Save SSH Key
-

Private Keys are used to connect to a remote server through a secure shell, called SSH.

-

You can use your own private key, or you can let Coolify to create one for you.

-

In both ways, you need to add the public version of your private key to the remote server's - ~/.ssh/authorized_keys file. +

+ Private keys are encrypted at rest in Coolify's database. +

+

+ Deploy the public key to + ~/.ssh/authorized_keys + on your target server for the specified user. +

+

+ Supports RSA, ED25519, ECDSA, and DSA key types in OpenSSH + format.

@elseif ($currentState === 'create-server') - + + - Please let me know your server details. + Provide connection details for your remote server. -
- - +
+ + +
+ - -
+ +
-
- +
- -
Non-root user is - experimental: docs. +

+ Non-root user support is experimental. + Learn more +

+
+
+
+ Validate Connection + + + +

+ Server must be accessible via SSH on the + specified port (default 22). +

+

+ Use IP addresses for direct connections or ensure + DNS resolution is configured. +

+

+ Root or sudo-enabled users recommended for full Docker + management capabilities. +

+
+ + @elseif ($currentState === 'validate-server') + + + + Coolify will automatically install Docker {{ $minDockerVersion }}+ if not present. + + +
+
+

Validation Steps

+
+
+
+ + + +
+
+
Test SSH Connection
+
Verify key-based authentication
+
+
+
+
+ + + +
+
+
Check OS Compatibility
+
Verify supported Linux distribution
+
+
+
+
+ + + +
+
+
Install Docker Engine
+
Auto-install if version {{ $minDockerVersion }}+ not + found
+
+
+
+
+ + + +
+
+
Configure Network
+
Set up Docker networks and proxy
- Continue - - - - @elseif ($currentState === 'validate-server') - - - I need to validate your server (connection, Docker Engine, etc) and configure if something is - missing for me. Are you okay with this? - - - - Validate & configure - - - - - Let's do it! - - + + + Server Validation + + + + + Start Validation + + +
-

This will install the latest Docker Engine on your server, configure a few things to be able - to run optimal.

Minimum Docker Engine version is: {{ $minDockerVersion }}

To - manually install - Docker - Engine, check this - documentation.

+

+ Coolify installs Docker Engine, Docker Compose, and + configures system requirements automatically. +

+

+ Minimum Docker Engine {{ $minDockerVersion }}.x + required. + Manual installation guide +

+

+ Sets up Docker networks, proxy configuration, and + resource monitoring. +

@elseif ($currentState === 'create-project') - + + - @if (count($projects) > 0) - You already have some projects. Do you want to use one of them or should I create a new one - for - you? + @if ($projects && $projects->count() > 0) + You have existing projects. Select one or create a new project to organize your resources. @else - Let's create an initial project for you. You can change all the details later on. + Create your first project to organize applications, databases, and services. @endif - Create new - project! -
- @if (count($projects) > 0) -
- +
+ + Create "My First Project" + + + @if ($projects && $projects->count() > 0) +
+
+
+
+
+ Or use existing +
+
+ + @foreach ($projects as $project) @endforeach - Use this Project + Use Selected Project @endif
-

Projects contain several resources combined into one virtual group. There are no - limitations on the number of projects you can add.

-

Each project should have at least one environment, this allows you to create a production & - staging version of the same application, but grouped separately.

+

+ Group related resources (apps, databases, services) + into logical projects. +

+

+ Each project includes a production environment by default. + Add staging, development, or custom environments as needed. +

+

+ Projects inherit team permissions and can be managed + collaboratively. +

@elseif ($currentState === 'create-resource') - - - Let's go to the new resource page, where you can create your first resource. - - -
Let's do - it!
-
- -

A resource could be an application, a database or a service (like WordPress).

-
-
+ +
+
+
+ + + +
+

Setup Complete!

+

+ Your server is connected and ready. Start deploying your first resource. +

+
+ +
+

+ What's Configured +

+
+
+
+ + + +
+
+
Server: {{ $createdServer->name }}
+
{{ $createdServer->ip }}
+
+
+
+
+ + + +
+
+
Project: {{ $createdProject->name }}
+
Production environment ready
+
+
+
+
+ + + +
+
+
Docker Engine
+
Installed and running
+
+
+
+
+ +
+ + Deploy Your First Resource + + +
+
@endif
-
-
-
Skip - onboarding
-
Restart - onboarding
+ + @if ($currentState !== 'welcome' && $currentState !== 'create-resource') +
+
+ + +
+ + + + + +
- - -
- Feedback -
-
- -
-
+ @endif