diff --git a/app/lib/prymn/apps.ex b/app/lib/prymn/apps.ex
new file mode 100644
index 0000000..db51b1c
--- /dev/null
+++ b/app/lib/prymn/apps.ex
@@ -0,0 +1,103 @@
+defmodule Prymn.Apps do
+  @moduledoc """
+  The Apps context.
+  """
+
+  import Ecto.Query, warn: false
+  alias Prymn.Repo
+  alias Prymn.Apps.App
+
+  @doc """
+  Returns the list of apps.
+
+  ## Examples
+
+      iex> list_apps()
+      [%App{}, ...]
+
+  """
+  def list_apps do
+    Repo.all(App)
+  end
+
+  @doc """
+  Gets a single app.
+
+  Raises `Ecto.NoResultsError` if the App does not exist.
+
+  ## Examples
+
+      iex> get_app!(123)
+      %App{}
+
+      iex> get_app!(456)
+      ** (Ecto.NoResultsError)
+
+  """
+  def get_app!(id), do: Repo.get!(App, id)
+
+  @doc """
+  Creates a app.
+
+  ## Examples
+
+      iex> create_app(%{field: value})
+      {:ok, %App{}}
+
+      iex> create_app(%{field: bad_value})
+      {:error, %Ecto.Changeset{}}
+
+  """
+  def create_app(attrs \\ %{}) do
+    %App{}
+    |> App.changeset(attrs)
+    |> Repo.insert()
+  end
+
+  @doc """
+  Updates a app.
+
+  ## Examples
+
+      iex> update_app(app, %{field: new_value})
+      {:ok, %App{}}
+
+      iex> update_app(app, %{field: bad_value})
+      {:error, %Ecto.Changeset{}}
+
+  """
+  def update_app(%App{} = app, attrs) do
+    app
+    |> App.changeset(attrs)
+    |> Repo.update()
+  end
+
+  @doc """
+  Deletes a app.
+
+  ## Examples
+
+      iex> delete_app(app)
+      {:ok, %App{}}
+
+      iex> delete_app(app)
+      {:error, %Ecto.Changeset{}}
+
+  """
+  def delete_app(%App{} = app) do
+    Repo.delete(app)
+  end
+
+  @doc """
+  Returns an `%Ecto.Changeset{}` for tracking app changes.
+
+  ## Examples
+
+      iex> change_app(app)
+      %Ecto.Changeset{data: %App{}}
+
+  """
+  def change_app(%App{} = app, attrs \\ %{}) do
+    App.changeset(app, attrs)
+  end
+end
diff --git a/app/lib/prymn/apps/app.ex b/app/lib/prymn/apps/app.ex
new file mode 100644
index 0000000..77a9943
--- /dev/null
+++ b/app/lib/prymn/apps/app.ex
@@ -0,0 +1,17 @@
+defmodule Prymn.Apps.App do
+  use Ecto.Schema
+  import Ecto.Changeset
+
+  schema "apps" do
+    field :name, :string
+
+    timestamps()
+  end
+
+  @doc false
+  def changeset(app, attrs) do
+    app
+    |> cast(attrs, [:name])
+    |> validate_required([:name])
+  end
+end
diff --git a/app/lib/prymn_web/components/create_app.ex b/app/lib/prymn_web/components/create_app.ex
new file mode 100644
index 0000000..cfeb448
--- /dev/null
+++ b/app/lib/prymn_web/components/create_app.ex
@@ -0,0 +1,78 @@
+defmodule PrymnWeb.CreateApp do
+  use PrymnWeb, :live_component
+
+  @impl true
+  def render(assigns) do
+    ~H"""
+    <div class="mx-auto max-w-2xl">
+      <fieldset>
+        <legend class="mb-6 border-b border-slate-200 pb-2 text-base font-semibold">App Type</legend>
+        <span>
+          <input
+            id="wordpress"
+            type="radio"
+            name="app_type"
+            value="wordpress"
+            class="peer hidden"
+            checked={@app_type == "wordpress"}
+          />
+          <label
+            for="wordpress"
+            class="inline-block cursor-pointer rounded p-5 shadow peer-checked:bg-black peer-checked:text-white"
+            phx-click={JS.patch(~p"/apps/new?app_type=wordpress")}
+          >
+            WordPress
+          </label>
+        </span>
+        <span>
+          <input
+            id="plain_html"
+            type="radio"
+            name="app_type"
+            value="plain_html"
+            class="peer hidden"
+            checked={@app_type == "plain_html"}
+          />
+          <label
+            for="plain_html"
+            class="inline-block cursor-pointer rounded p-5 shadow peer-checked:bg-black peer-checked:text-white"
+            phx-click={JS.patch(~p"/apps/new?app_type=plain_html")}
+          >
+            Plain HTML
+          </label>
+        </span>
+      </fieldset>
+      <.wordpress_app_form :if={assigns.app_type == "wordpress"} servers={assigns[:servers]} />
+    </div>
+    """
+  end
+
+  defp wordpress_app_form(assigns) do
+    ~H"""
+    <.simple_form id="test" for={nil}>
+      <.input
+        :if={assigns.servers != nil}
+        id="server"
+        type="select"
+        name="server"
+        prompt="Select a server to host this app..."
+        options={[]}
+        value={nil}
+        label="Hosting Server"
+      />
+      <.input id="name" type="text" name="app_name" value={nil} label="WordPress Site Name" required />
+      <.input id="domain" type="text" name="domain" value={nil} label="Domain Name" required />
+      <.input type="checkbox" name="create_database?" label="Create a new database?" />
+      <.input type="text" name="db_name" value={nil} label="Database name" />
+
+      <.input type="select" name="db_name" value={nil} options={["db1", "db2"]} label="Database" />
+
+      <.input type="text" name="admin_username" value={nil} label="Admin Username" />
+      <.input type="email" name="admin_email" value={nil} label="Admin Email" />
+      <.input type="password" name="admin_password" value={nil} label="Admin Password" />
+
+      <.button type="submit">Create</.button>
+    </.simple_form>
+    """
+  end
+end
diff --git a/app/lib/prymn_web/live/app_index_live.ex b/app/lib/prymn_web/live/app_index_live.ex
new file mode 100644
index 0000000..76ad2d7
--- /dev/null
+++ b/app/lib/prymn_web/live/app_index_live.ex
@@ -0,0 +1,63 @@
+defmodule PrymnWeb.AppIndexLive do
+  use PrymnWeb, :live_view
+
+  @impl true
+  def mount(_, _, socket) do
+    apps = Prymn.Apps.list_apps()
+    servers = Prymn.Servers.list_servers()
+
+    {:ok,
+     socket
+     |> assign(:servers, servers)
+     |> assign(:apps, apps)}
+  end
+
+  @impl true
+  def render(assigns) do
+    ~H"""
+    <%= cond do %>
+      <% assigns.live_action == :new -> %>
+        <.back navigate={~p"/apps"}>Go back</.back>
+        <.live_component
+          id={:new}
+          module={PrymnWeb.CreateApp}
+          app_type={assigns[:app_type]}
+          servers={@servers}
+        />
+      <% assigns.apps == [] -> %>
+        <.onboarding />
+      <% true -> %>
+        <div class="mx-auto max-w-2xl">
+          <.header>
+            Live Apps
+            <:subtitle>
+              All of your apps accross all projects.
+            </:subtitle>
+          </.header>
+        </div>
+    <% end %>
+    """
+  end
+
+  @impl true
+  def handle_params(%{"app_type" => app_type}, _, socket) do
+    {:noreply, assign(socket, app_type: app_type)}
+  end
+
+  def handle_params(_, _, socket) do
+    {:noreply,
+     socket
+     |> assign(:page_title, (socket.assigns.live_action == :new && "New App") || "Apps")}
+  end
+
+  defp onboarding(assigns) do
+    ~H"""
+    <div class="mx-auto max-w-2xl text-center">
+      <h1 class="mb-5 text-3xl font-medium">You have no Apps.</h1>
+      <.button type="link" patch={~p"/apps/new"}>
+        <.icon class="mr-2 h-6 w-6" name="hero-plus" /> Create your first App!
+      </.button>
+    </div>
+    """
+  end
+end
diff --git a/app/lib/prymn_web/router.ex b/app/lib/prymn_web/router.ex
index 21ccd5c..ef30200 100644
--- a/app/lib/prymn_web/router.ex
+++ b/app/lib/prymn_web/router.ex
@@ -37,6 +37,9 @@ defmodule PrymnWeb.Router do
       live "/servers", ServerLive.Index, :index
       live "/servers/new", ServerLive.Index, :new
       live "/servers/:id", ServerLive.Show
+
+      live "/apps", AppIndexLive
+      live "/apps/new", AppIndexLive, :new
     end
   end
 
diff --git a/app/test/prymn/apps_test.exs b/app/test/prymn/apps_test.exs
new file mode 100644
index 0000000..1d31f6e
--- /dev/null
+++ b/app/test/prymn/apps_test.exs
@@ -0,0 +1,59 @@
+defmodule Prymn.AppsTest do
+  use Prymn.DataCase
+
+  alias Prymn.Apps
+
+  describe "apps" do
+    alias Prymn.Apps.App
+
+    import Prymn.AppsFixtures
+
+    @invalid_attrs %{name: nil}
+
+    test "list_apps/0 returns all apps" do
+      app = app_fixture()
+      assert Apps.list_apps() == [app]
+    end
+
+    test "get_app!/1 returns the app with given id" do
+      app = app_fixture()
+      assert Apps.get_app!(app.id) == app
+    end
+
+    test "create_app/1 with valid data creates a app" do
+      valid_attrs = %{name: "some name"}
+
+      assert {:ok, %App{} = app} = Apps.create_app(valid_attrs)
+      assert app.name == "some name"
+    end
+
+    test "create_app/1 with invalid data returns error changeset" do
+      assert {:error, %Ecto.Changeset{}} = Apps.create_app(@invalid_attrs)
+    end
+
+    test "update_app/2 with valid data updates the app" do
+      app = app_fixture()
+      update_attrs = %{name: "some updated name"}
+
+      assert {:ok, %App{} = app} = Apps.update_app(app, update_attrs)
+      assert app.name == "some updated name"
+    end
+
+    test "update_app/2 with invalid data returns error changeset" do
+      app = app_fixture()
+      assert {:error, %Ecto.Changeset{}} = Apps.update_app(app, @invalid_attrs)
+      assert app == Apps.get_app!(app.id)
+    end
+
+    test "delete_app/1 deletes the app" do
+      app = app_fixture()
+      assert {:ok, %App{}} = Apps.delete_app(app)
+      assert_raise Ecto.NoResultsError, fn -> Apps.get_app!(app.id) end
+    end
+
+    test "change_app/1 returns a app changeset" do
+      app = app_fixture()
+      assert %Ecto.Changeset{} = Apps.change_app(app)
+    end
+  end
+end
diff --git a/app/test/support/fixtures/apps_fixtures.ex b/app/test/support/fixtures/apps_fixtures.ex
new file mode 100644
index 0000000..83e103f
--- /dev/null
+++ b/app/test/support/fixtures/apps_fixtures.ex
@@ -0,0 +1,20 @@
+defmodule Prymn.AppsFixtures do
+  @moduledoc """
+  This module defines test helpers for creating
+  entities via the `Prymn.Apps` context.
+  """
+
+  @doc """
+  Generate a app.
+  """
+  def app_fixture(attrs \\ %{}) do
+    {:ok, app} =
+      attrs
+      |> Enum.into(%{
+        name: "some name"
+      })
+      |> Prymn.Apps.create_app()
+
+    app
+  end
+end
diff --git a/priv/repo/migrations/20231119141943_create_apps.exs b/priv/repo/migrations/20231119141943_create_apps.exs
new file mode 100644
index 0000000..7b1e392
--- /dev/null
+++ b/priv/repo/migrations/20231119141943_create_apps.exs
@@ -0,0 +1,11 @@
+defmodule Prymn.Repo.Migrations.CreateApps do
+  use Ecto.Migration
+
+  def change do
+    create table(:apps) do
+      add :name, :string
+
+      timestamps()
+    end
+  end
+end