2025-10-08 18:47:50 +00:00
< div class = " w-full " >
@ if ( $limit_reached )
< x - limit - reached name = " servers " />
@ else
2025-10-09 08:41:29 +00:00
@ if ( $current_step === 1 )
2025-10-09 10:53:57 +00:00
< div class = " flex flex-col w-full gap-4 " >
2026-07-08 10:20:56 +00:00
< div class = " text-sm text-neutral-500 dark:text-neutral-400 " >
Don ' t have a Hetzner account ? < a href = " https://coolify.io/hetzner " target = " _blank "
class = " underline dark:text-white " > Sign up here </ a >.
< span class = " text-xs " > Coolify ' s affiliate link , only for new accounts - it supports both of us .</ span >
</ div >
2025-10-09 08:41:29 +00:00
@ if ( $available_tokens -> count () > 0 )
2026-07-08 10:20:56 +00:00
< div class = " grid gap-3 md:grid-cols-2 " >
@ foreach ( $available_tokens as $token )
< a class = " coolbox group text-left " wire : key = " hetzner-token- { { $token->id }} "
href = " { { route('server.create.token', ['type' => 'hetzner', 'token_uuid' => $token->uuid ]) }} " {{ wireNavigate () }} >
< div class = " flex flex-col justify-center mx-6 " >
< div class = " box-title " >
2025-10-09 10:53:57 +00:00
{{ $token -> name ? ? 'Hetzner Token' }}
2026-07-08 10:20:56 +00:00
</ div >
< div class = " box-description " >
Use this token to create a Hetzner server .
</ div >
</ div >
</ a >
@ endforeach
</ div >
@ else
< div class = " w-full max-w-2xl " >
< x - modal - input title = " Add Hetzner Token " >
< x - slot : content >
< div class = " coolbox group cursor-pointer " >
< div class = " flex items-center gap-4 mx-6 " >
< div
class = " flex size-10 shrink-0 items-center justify-center rounded-full bg-coollabs/10 text-coollabs dark:bg-warning/20 dark:text-warning " >
< svg class = " size-6 " xmlns = " http://www.w3.org/2000/svg " fill = " none "
viewBox = " 0 0 24 24 " stroke - width = " 1.5 " stroke = " currentColor " >
< path stroke - linecap = " round " stroke - linejoin = " round "
d = " M12 4.5v15m7.5-7.5h-15 " />
</ svg >
</ div >
< div class = " flex flex-col justify-center " >
< div class = " box-title " > Add a new token </ div >
< div class = " box-description " >
Add a Hetzner API token to create servers from your account .
</ div >
</ div >
</ div >
</ div >
</ x - slot : content >
< livewire : security . cloud - provider - token - form : modal_mode = " true " provider = " hetzner "
wire : key = " new-server-empty-token-hetzner " />
</ x - modal - input >
2025-10-09 08:41:29 +00:00
</ div >
2025-10-09 10:53:57 +00:00
@ endif
</ div >
2025-10-09 08:41:29 +00:00
@ elseif ( $current_step === 2 )
2026-07-08 10:20:56 +00:00
< div wire : init = " loadHetznerData " >
2025-10-09 08:41:29 +00:00
@ if ( $loading_data )
< div class = " flex items-center justify-center py-8 " >
2026-07-08 10:20:56 +00:00
< x - loading text = " Loading Hetzner data... " />
2025-10-09 08:41:29 +00:00
</ div >
2026-07-08 10:58:27 +00:00
@ elseif ( $provider_data_error )
< div class = " flex flex-col gap-4 rounded-lg border border-error bg-error/10 p-4 " >
< div >
< h3 > Unable to load Hetzner details </ h3 >
< p class = " text-sm text-neutral-700 dark:text-neutral-300 " >
Coolify could not fetch Hetzner data with the selected token . The token may have been
deleted , revoked , or no longer has access .
</ p >
</ div >
< pre class = " whitespace-pre-wrap break-words text-sm text-error " > {{ $provider_data_error }} </ pre >
< div >
< a class = " button " href = " { { route('server.create.type', ['type' => 'hetzner']) }} " {{ wireNavigate () }} >
Select another token
</ a >
</ div >
</ div >
2025-10-09 08:41:29 +00:00
@ else
< form class = " flex flex-col w-full gap-2 " wire : submit = 'submit' >
< div >
< x - forms . input id = " server_name " label = " Server Name " helper = " A friendly name for your server. " />
</ div >
< div >
2025-10-11 09:25:42 +00:00
< x - forms . select label = " Location " id = " selected_location " wire : model . live = " selected_location " required >
2025-10-09 08:41:29 +00:00
< option value = " " > Select a location ...</ option >
@ foreach ( $locations as $location )
< option value = " { { $location['name'] }} " >
{{ $location [ 'city' ] }} - {{ $location [ 'country' ] }}
</ option >
@ endforeach
</ x - forms . select >
</ div >
< div >
2025-10-11 09:25:42 +00:00
< x - forms . select label = " Server Type " id = " selected_server_type " wire : model . live = " selected_server_type "
2025-10-21 22:13:55 +00:00
helper = " Learn more about <a class='inline-block underline dark:text-white' href='https://www.hetzner.com/cloud/' target='_blank'>Hetzner server types</a> "
2025-10-11 09:25:42 +00:00
required : disabled = " ! $selected_location " >
2025-10-09 10:53:57 +00:00
< option value = " " >
{{ $selected_location ? 'Select a server type...' : 'Select a location first' }}
</ option >
2025-10-09 08:41:29 +00:00
@ foreach ( $this -> availableServerTypes as $serverType )
< option value = " { { $serverType['name'] }} " >
{{ $serverType [ 'description' ] }} -
2025-10-13 20:21:35 +00:00
{{ $serverType [ 'cores' ] }} vCPU
@ if ( isset ( $serverType [ 'cpu_vendor_info' ]) && $serverType [ 'cpu_vendor_info' ])
({{ $serverType [ 'cpu_vendor_info' ] }})
@ endif
2026-04-28 13:37:26 +00:00
, {{ $serverType [ 'memory' ] }} GB RAM ,
2025-10-09 08:41:29 +00:00
{{ $serverType [ 'disk' ] }} GB
@ if ( isset ( $serverType [ 'architecture' ]))
2025-10-13 20:21:35 +00:00
[{{ $serverType [ 'architecture' ] }}]
2025-10-09 08:41:29 +00:00
@ endif
@ if ( isset ( $serverType [ 'prices' ]))
-
€ {{ number_format ( $serverType [ 'prices' ][ 0 ][ 'price_monthly' ][ 'gross' ] ? ? 0 , 2 ) }} / mo
@ endif
</ option >
@ endforeach
</ x - forms . select >
</ div >
< div >
< x - forms . select label = " Image " id = " selected_image " required : disabled = " ! $selected_server_type " >
2025-10-09 10:53:57 +00:00
< option value = " " >
{{ $selected_server_type ? 'Select an image...' : 'Select a server type first' }}
</ option >
2025-10-09 08:41:29 +00:00
@ foreach ( $this -> availableImages as $image )
< option value = " { { $image['id'] }} " >
{{ $image [ 'description' ] ? ? $image [ 'name' ] }}
@ if ( isset ( $image [ 'architecture' ]))
({{ $image [ 'architecture' ] }})
@ endif
</ option >
@ endforeach
</ x - forms . select >
</ div >
< div >
2025-10-10 13:53:17 +00:00
@ if ( $private_keys -> count () === 0 )
< div class = " flex flex-col gap-2 " >
< label class = " flex gap-1 items-center mb-1 text-sm font-medium " >
Private Key
< x - highlighted text = " * " />
</ label >
< div
2025-11-28 15:23:32 +00:00
class = " p-4 border border-warning-500 dark:border-warning-600 rounded bg-warning-50 dark:bg-warning-900/10 " >
2025-10-10 13:53:17 +00:00
< p class = " text-sm mb-3 text-neutral-700 dark:text-neutral-300 " >
No private keys found . You need to create a private key to continue .
</ p >
2025-10-11 09:25:42 +00:00
< x - modal - input buttonTitle = " Create New Private Key " title = " New Private Key " isHighlightedButton >
2025-10-10 13:53:17 +00:00
< livewire : security . private - key . create : modal_mode = " true " from = " server " />
</ x - modal - input >
</ div >
</ div >
@ else
< x - forms . select label = " Private Key " id = " private_key_id " required >
< option value = " " > Select a private key ...</ option >
@ foreach ( $private_keys as $key )
< option value = " { { $key->id }} " >
{{ $key -> name }}
</ option >
@ endforeach
</ x - forms . select >
< p class = " mt-1 text-xs text-neutral-500 dark:text-neutral-400 " >
This SSH key will be automatically added to your Hetzner account and used to access the
server .
</ p >
@ endif
2025-10-09 08:41:29 +00:00
</ div >
2026-07-07 12:36:11 +00:00
< x - dropdown inline panelClass = " max-h-[55vh] overflow-y-auto scrollbar " >
< x - slot : title >
< span class = " text-sm font-medium " > Advanced Hetzner options </ span >
< span class = " mt-1 text-xs text-neutral-500 dark:text-neutral-400 " > SSH keys , firewalls , private networks , backups , and cloud - init .</ span >
@ if ( count ( $this -> advancedHetznerOptionsSummary ) > 0 )
< span class = " mt-1 flex flex-wrap gap-1.5 " >
@ foreach ( $this -> advancedHetznerOptionsSummary as $summaryItem )
< span class = " rounded bg-neutral-200 px-2 py-0.5 text-xs text-neutral-700 dark:bg-coolgray-100 dark:text-neutral-300 " >
{{ $summaryItem }}
</ span >
@ endforeach
</ span >
@ endif
</ x - slot >
2025-10-10 16:22:25 +00:00
2026-07-07 12:36:11 +00:00
< div class = " flex w-full flex-col gap-4 p-3 " >
< div >
< x - forms . datalist label = " Extra SSH Keys " id = " selectedHetznerSshKeyIds "
helper = " Select existing SSH keys from your Hetzner account to add to this server. The Coolify SSH key will be automatically added. "
: multiple = " true " : disabled = " count( $hetznerSshKeys ) === 0 " : placeholder = " count( $hetznerSshKeys ) > 0
? 'Search and select SSH keys...'
: 'No SSH keys found in Hetzner account' " >
@ foreach ( $hetznerSshKeys as $sshKey )
< option value = " { { $sshKey['id'] }} " >
{{ $sshKey [ 'name' ] }} - {{ substr ( $sshKey [ 'fingerprint' ], 0 , 20 ) }} ...
</ option >
@ endforeach
</ x - forms . datalist >
</ div >
2026-04-17 10:41:10 +00:00
2026-07-07 12:36:11 +00:00
< div class = " grid grid-cols-1 gap-4 md:grid-cols-2 " >
< x - forms . datalist label = " Firewalls " id = " selectedHetznerFirewallIds "
helper = " Optionally apply existing Hetzner firewalls when the server is created. "
: multiple = " true " : disabled = " count( $hetznerFirewalls ) === 0 " : placeholder = " count( $hetznerFirewalls ) > 0
? 'Search and select firewalls...'
: 'No firewalls found in Hetzner account' " >
@ foreach ( $hetznerFirewalls as $firewall )
< option value = " { { $firewall['id'] }} " >
{{ $firewall [ 'name' ] }}
@ if ( isset ( $firewall [ 'rules' ]))
- {{ count ( $firewall [ 'rules' ]) }} rules
@ endif
</ option >
@ endforeach
</ x - forms . datalist >
2026-04-17 10:41:10 +00:00
2026-07-07 12:36:11 +00:00
< x - forms . datalist label = " Private Networks " id = " selectedHetznerNetworkIds "
helper = " Optionally attach one or more private networks. Networks are filtered to the selected location's network zone when possible. "
: multiple = " true " : disabled = " count( $this->availableNetworks ) === 0 " : placeholder = " count( $this->availableNetworks ) > 0
? 'Search and select networks...'
: 'No compatible networks found' " >
@ foreach ( $this -> availableNetworks as $network )
< option value = " { { $network['id'] }} " >
{{ $network [ 'name' ] }} - {{ $network [ 'ip_range' ] }}
</ option >
@ endforeach
</ x - forms . datalist >
</ div >
2025-10-10 16:22:25 +00:00
2026-07-07 12:36:11 +00:00
< div class = " grid grid-cols-1 gap-2 md:grid-cols-3 " >
< x - forms . checkbox id = " enable_ipv4 " label = " Enable IPv4 "
helper = " Enable public IPv4 address for this server " fullWidth />
< x - forms . checkbox id = " enable_ipv6 " label = " Enable IPv6 "
helper = " Enable public IPv6 address for this server " fullWidth />
< x - forms . checkbox id = " enable_backups " label = " Enable Hetzner Backups " fullWidth
helper = " Hetzner bills backups at an additional 20% of the server monthly fee { { $this->selectedServerBackupSurcharge ? ' (about ' . $this->selectedServerBackupSurcharge . '/mo for the selected server type)' : '' }}. " />
</ div >
2026-04-28 13:37:26 +00:00
2026-07-07 12:36:11 +00:00
< div class = " flex flex-col gap-2 " >
@ if ( ! $show_cloud_init_script && empty ( $cloud_init_script ) && empty ( $selected_cloud_init_script_id ))
< div >
< x - forms . button type = " button " wire : click = " showCloudInitScript " >
Add cloud - init script
</ x - forms . button >
</ div >
@ else
< div class = " flex justify-between items-center gap-2 " >
< label class = " text-sm font-medium w-32 " > Cloud - Init Script </ label >
@ if ( $saved_cloud_init_scripts -> count () > 0 )
< div class = " flex items-center gap-2 flex-1 " >
< x - forms . select wire : model . live = " selected_cloud_init_script_id " label = " " helper = " " >
< option value = " " > Load saved script ...</ option >
@ foreach ( $saved_cloud_init_scripts as $script )
< option value = " { { $script->id }} " > {{ $script -> name }} </ option >
@ endforeach
</ x - forms . select >
< x - forms . button type = " button " wire : click = " clearCloudInitScript " >
Clear
</ x - forms . button >
</ div >
@ else
< x - forms . button type = " button " wire : click = " clearCloudInitScript " >
Remove
</ x - forms . button >
@ endif
</ div >
< x - forms . textarea id = " cloud_init_script " label = " "
helper = " Add a cloud-init script to run when the server is created. See Hetzner's documentation for details. "
rows = " 8 " />
2025-10-10 17:37:16 +00:00
2026-07-07 12:36:11 +00:00
< div class = " flex items-center gap-2 " >
< x - forms . checkbox id = " save_cloud_init_script " label = " Save this script for later use " />
@ if ( $save_cloud_init_script )
< div class = " flex-1 " >
< x - forms . input id = " cloud_init_script_name " label = " " placeholder = " Script name... " />
</ div >
@ endif
</ div >
@ endif
2025-10-11 09:16:28 +00:00
</ div >
2025-10-11 08:55:14 +00:00
</ div >
2026-07-07 12:36:11 +00:00
</ x - dropdown >
2025-10-10 17:37:16 +00:00
2025-10-10 13:53:17 +00:00
< x - forms . button isHighlighted canGate = " create " : canResource = " App \ Models \ Server::class " type = " submit "
: disabled = " ! $private_key_id " >
2025-10-10 16:50:44 +00:00
Buy & Create Server {{ $this -> selectedServerPrice ? ' (' . $this -> selectedServerPrice . '/mo)' : '' }}
2025-10-09 08:41:29 +00:00
</ x - forms . button >
</ form >
@ endif
2026-07-08 10:20:56 +00:00
</ div >
2025-10-09 08:41:29 +00:00
@ endif
2025-10-08 18:47:50 +00:00
@ endif
2026-04-17 10:41:10 +00:00
</ div >