diff --git a/app/Http/Controllers/OrganizationController.php b/app/Http/Controllers/OrganizationController.php
index bd8b9647de45d538f5fd8ea1904f9eb0d0ac5500..e944749072ff7442e8db796840ef44f8eec80337 100644
--- a/app/Http/Controllers/OrganizationController.php
+++ b/app/Http/Controllers/OrganizationController.php
@@ -77,12 +77,6 @@ class OrganizationController extends Controller
         $this->authorize("show", $organization);
         return view("organization.show", array("organization" => $organization));
     }
-    
-    public function rack(Organization $organization)
-    {
-        $this->authorize("show", $organization);
-        return view("organization.rack", ["organization" => $organization]);
-    }
 
     public function dashboard(Organization $organization)
     {
diff --git a/app/Http/Controllers/RackController.php b/app/Http/Controllers/RackController.php
new file mode 100644
index 0000000000000000000000000000000000000000..a276dbeb33f3597ab0e994dba625f07afd31d295
--- /dev/null
+++ b/app/Http/Controllers/RackController.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Rack;
+use App\Organization;
+use Illuminate\Http\Request;
+
+class RackController extends Controller
+{
+    /**
+     * Display a listing of the resource.
+     *
+     */
+    public function index(Organization $organization)
+    {
+        $this->authorize("show", $organization);
+        return view("organization.rack", ["organization" => $organization]);
+    }
+
+    /**
+     * Show the form for creating a new resource.
+     *
+     */
+    public function create(Organization $organization)
+    {
+        return view("rack.edit", [
+            "rack" => new Rack(),
+            "organization" => $organization]);
+    }
+
+    /**
+     * Store a newly created resource in storage.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     */
+    public function store(Organization $organization, Request $request)
+    {
+        return $this->save($organization, $request, new Rack());
+    }
+    
+    public function save(Organization $organization, Request $request, Rack $rack)
+    {
+        $request->validate([
+            "name" => "required|string",
+            "height" => "required|integer|min:1|max:50"
+        ]);
+        
+        $rack->name = $request->name;
+        $rack->height = $request->height;
+        $rack->organization_id = $organization->id;
+        $rack->save();
+        
+        return redirect(action("RackController@index", ["organization" => $organization]));
+    }
+
+    /**
+     * Remove the specified resource from storage.
+     *
+     * @param  \App\Rack  $rack
+     */
+    public function destroy(Rack $rack)
+    {
+        //
+    }
+}
diff --git a/app/Http/Controllers/ServerController.php b/app/Http/Controllers/ServerController.php
index 07ad995b1c88dd00a24a88b8f94929e3dd12f44c..c03c632d94eb953997186cd3f71113fec61deeac 100644
--- a/app/Http/Controllers/ServerController.php
+++ b/app/Http/Controllers/ServerController.php
@@ -22,6 +22,7 @@ class ServerController extends Controller
             'name' => 'required|string|regex:/^[a-zA-Z0-9\s\-\.]+$/|max:255',
             "organization_id" => Rule::in(Auth::user()->organizations->modelKeys()),
             "description" => 'nullable|string',
+            "rack_id" => "required|integer",
             "size" => "nullable|int|min:0|max:48",
             "position" => "nullable|int|min:0|max:48"];
     }
@@ -94,6 +95,12 @@ class ServerController extends Controller
         $server->description = $request->description;
         $server->size = $request->size;
         $server->position = $request->position;
+        $server->rack_id = $request->rack_id;
+        
+        if ($server->rack_id == 0) {
+            $server->rack_id = null;
+        }
+        
         $server->save();
 
         return redirect(action("ServerController@show", ["server" => $server]));
diff --git a/app/Organization.php b/app/Organization.php
index fdf3c54019e10a3e86e9c9eebc917f97a924dbce..a3dfdfb11d2feea667c4c88c69ed835713bbe1a5 100644
--- a/app/Organization.php
+++ b/app/Organization.php
@@ -43,12 +43,18 @@ class Organization extends Model
 
     public function users()
     {
-        return $this->belongsToMany("App\User");
+        return $this->belongsToMany(User::class);
     }
 
     public function servers()
     {
-        return $this->hasMany("App\Server");
+        return $this->hasMany(Server::class);
+    }
+    
+    
+    public function racks()
+    {
+        return $this->hasMany(Rack::class);
     }
 
     public function url() : string
diff --git a/app/Rack.php b/app/Rack.php
new file mode 100644
index 0000000000000000000000000000000000000000..b6ad5f77a536b2440dc1a6a5c58cd9424501957c
--- /dev/null
+++ b/app/Rack.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+class Rack extends Model
+{
+    public function servers()
+    {
+        return $this->hasMany(Server::class);
+    }
+}
diff --git a/app/Server.php b/app/Server.php
index df4267431c5a31ebc5351abbfafd0c353d1ae354..bcac738220a873d2b6d941adeddcecb932dc6566 100644
--- a/app/Server.php
+++ b/app/Server.php
@@ -199,4 +199,9 @@ class Server extends Model
         $converter = new CommonMarkConverter();
         return $converter->convertToHtml($this->description ?? '');
     }
+    
+    public function rack()
+    {
+        return $this->belongsTo(Rack::class);
+    }
 }
diff --git a/database/migrations/2024_04_04_110616_create_racks_table.php b/database/migrations/2024_04_04_110616_create_racks_table.php
new file mode 100644
index 0000000000000000000000000000000000000000..28f1a344feba7c8acbad7606558896fabc2f6b1a
--- /dev/null
+++ b/database/migrations/2024_04_04_110616_create_racks_table.php
@@ -0,0 +1,34 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateRacksTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('racks', function (Blueprint $table) {
+            $table->bigIncrements('id');
+            $table->timestamps();
+            $table->integer("organization_id")->references("id")->on("organizations");
+            $table->string("name");
+            $table->integer("height");
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('racks');
+    }
+}
diff --git a/database/migrations/2024_04_04_134753_servers_add_rack_id.php b/database/migrations/2024_04_04_134753_servers_add_rack_id.php
new file mode 100644
index 0000000000000000000000000000000000000000..fa4fea8370eb9e9812a3b9151bb432fcab4ad254
--- /dev/null
+++ b/database/migrations/2024_04_04_134753_servers_add_rack_id.php
@@ -0,0 +1,35 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class ServersAddRackId extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('servers', function (Blueprint $table) {
+            $table->bigInteger("rack_id")
+                    ->nullable()
+                    ->references("id")->on("servers")
+                    ->onDelete('set null');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('servers', function (Blueprint $table) {
+            //
+        });
+    }
+}
diff --git a/resources/views/organization/rack.blade.php b/resources/views/organization/rack.blade.php
index a3d359e71eac5de85c1cc2db9a909f55cad8a5d1..ef11631e46f5b4501114ed7e731446dac3c9e976 100644
--- a/resources/views/organization/rack.blade.php
+++ b/resources/views/organization/rack.blade.php
@@ -1,20 +1,25 @@
 @extends('layouts.app')
 
 @section('content')
-<div class="container-fluid pb-4">
+<div class="container-fluid pb-4 text-center">
     
-    <div class="rack size-42u">
-        @for ($i = 0; $i < 42; $i++)
-        <div class="slot" style="bottom: {{ 2*$i }}rem">
+    @foreach ($organization->racks as $rack)
+    <div class="rack size-{{ $rack->height + 1 }}u">
+        <div class="slot" style="top: 0">
+            {{ $rack->name }}
+        </div>
+        
+        @for ($i = 0; $i < $rack->height; $i++)
+        <div class="slot text-left" style="bottom: {{ 2*$i }}rem">
             {{ $i + 1 }}
         </div>
         @endfor
 
-        @foreach ($organization->servers as $server)
+        @foreach ($rack->servers as $server)
         @if ($server->size == 0)
             @continue
         @endif
-        <div class="server size-{{ $server->size }}u"
+        <div class="server text-left size-{{ $server->size }}u"
              style="bottom: {{ 2*($server->position - 1) }}rem;">
             <p>
                 <a href="{{ $server->getUrlAttribute() }}"
@@ -26,13 +31,6 @@
         </div>
         @endforeach
     </div>
-
-    <div class="rack size-12u">
-        @for ($i = 0; $i < 12; $i++)
-        <div class="slot" style="bottom: {{ 2*$i }}rem">
-            {{ $i + 1 }}
-        </div>
-        @endfor
-    </div>
+    @endforeach
 </div>
 @endsection
diff --git a/resources/views/organization/show.blade.php b/resources/views/organization/show.blade.php
index 12e51f90bd62c03260db35954955f5b17c3c7146..6af5ca944c6d7e9eb5f03afe23f636c62dd72527 100644
--- a/resources/views/organization/show.blade.php
+++ b/resources/views/organization/show.blade.php
@@ -30,7 +30,7 @@
         </a>
         
         <a class="btn btn-primary btn-sm"
-           href="{{ action("OrganizationController@rack", ["organization" => $organization]) }}">
+           href="{{ action("RackController@index", ["organization" => $organization]) }}">
             <i class="fas fa-server"></i> Rack view
         </a>
     </p>
diff --git a/resources/views/rack/edit.blade.php b/resources/views/rack/edit.blade.php
new file mode 100644
index 0000000000000000000000000000000000000000..95d0e0475ee705204234727de76947542521cda0
--- /dev/null
+++ b/resources/views/rack/edit.blade.php
@@ -0,0 +1,58 @@
+@extends('layouts.app')
+
+@section('content')
+<div class="container">
+    <div class="card">
+        <div class="card-header">Rack</div>
+
+        <div class="card-body">
+            @if (!$rack->exists)
+            <form method="POST" action="{{ action("RackController@store", ["organization" => $organization]) }}">
+            @else
+            <form method="POST"
+                  action="{{ action("RackController@update", ["rack" => $rack]) }}">
+            {{ method_field("PUT") }}
+            @endif
+                {{ csrf_field() }}
+
+                <div class="form-group">
+                    <label for="name">Name</label>
+
+                    <input id="name" type="text"
+                           class="form-control{{ $errors->has('name') ? ' is-invalid' : '' }}"
+                           name="name"
+                           value="{{ old('name', $rack->name) }}" autofocus>
+
+                    @if ($errors->has('name'))
+                        <span class="invalid-feedback">
+                            <strong>{{ $errors->first('name') }}</strong>
+                        </span>
+                    @endif
+                </div>
+
+                <div class="form-group">
+                    <label for="height">Height</label>
+
+                    <input id="height"
+                           type="number" min="1" max="50" step="1"
+                           class="form-control{{ $errors->has('height') ? ' is-invalid' : '' }}"
+                           name="height"
+                           value="{{ old('height', $rack->height) }}" autofocus>
+
+                    @if ($errors->has('name'))
+                        <span class="invalid-feedback">
+                            <strong>{{ $errors->first('name') }}</strong>
+                        </span>
+                    @endif
+                </div>
+
+                <div class="form-group">
+                    <button type="submit" class="btn btn-primary btn-sm">
+                        <i class="fas fa-check"></i> Save
+                    </button>
+                </div>
+            </form>
+        </div>
+    </div>
+</div>
+@endsection
diff --git a/resources/views/server/edit.blade.php b/resources/views/server/edit.blade.php
index 9781e8b0f72fef3a41b6b0f69a60d65cfd87fe0d..6b5e22a56ef3d3999dfc71874f4bc5762e1d6743 100644
--- a/resources/views/server/edit.blade.php
+++ b/resources/views/server/edit.blade.php
@@ -28,10 +28,9 @@
                                 @foreach (Auth::user()->organizations as $organization)
                                 <option value="{{ $organization->id }}">{{ $organization->name }}</option>
                                 @endforeach
-
                             </select>
 
-                            @if ($errors->has('name'))
+                            @if ($errors->has('organization_id'))
                                 <span class="invalid-feedback">
                                     <strong>{{ $errors->first('organization_id') }}</strong>
                                 </span>
@@ -69,6 +68,26 @@
                             @endif
                         </div>
                         
+                        <div class="form-group">
+                            <label for="rack_id">Rack</label>
+
+                            <select id="rack_id"
+                                   class="form-control{{ $errors->has('rack_id') ? ' is-invalid' : '' }}"
+                                   name="rack_id">
+                                
+                                <option value="0">--</option>
+                                @foreach ($organization->racks as $rack)
+                                <option value="{{ $rack->id }}">{{ $rack->name }}</option>
+                                @endforeach
+                            </select>
+
+                            @if ($errors->has('organization_id'))
+                                <span class="invalid-feedback">
+                                    <strong>{{ $errors->first('organization_id') }}</strong>
+                                </span>
+                            @endif
+                        </div>
+                        
                         <div class="form-group">
                             <label for="size">Form factor</label>
 
diff --git a/resources/views/server/show.blade.php b/resources/views/server/show.blade.php
index 255b1097e9a3612c4c9c3f5f8c8e5d0e79634dcf..cf6eda93307d6806ba3b33759d0778a95aff66c3 100644
--- a/resources/views/server/show.blade.php
+++ b/resources/views/server/show.blade.php
@@ -44,6 +44,16 @@ window.monitorServerToken = "{{ $server->read_token }}";
             </p>
 
             <p>Uptime: {{ $server->info()->uptime() }}</p>
+            
+            @if (! is_null($server->rack_id))
+            <p>
+                Rack
+                <b>
+                    {{ $server->rack->name }}#{{ $server->position }} 
+                    [{{ $server->size }}u]
+                </b>
+            </p>
+            @endif
         </div>
     </div>
     
diff --git a/routes/web.php b/routes/web.php
index 3282999c41d29b5e802dacaa7d2b7172b31ce6ad..2a3e64e965bb0841b97cdec648b7e30f4fd4815f 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -51,8 +51,10 @@ Route::get(
     'app/organizations/{organization}/{token}/dashboard.json',
     'OrganizationDashboardController@json'
 );
-Route::get('app/organizations/{organization}/rack', 'OrganizationController@rack');
+
 Route::resource('app/organizations', 'OrganizationController');
 Route::resource("app/organizations.user", "OrganizationUserController")->only(["create", "store", "destroy"]);
+Route::resource("app/organizations.rack", 'RackController')->only(["index", "create", "store", "destroy"]);
 Route::resource('app/servers', 'ServerController')->except(["index"]);
+
 Route::get("app/records/{record}", "RecordController@show");