docs: refactor and update
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 0000000..f76234c
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,34 @@
+name: docs
+
+on:
+  pull_request:
+    paths:
+      - 'docs/**'
+  push:
+    branches:
+      - main
+    paths:
+      - 'docs/**'
+
+permissions:
+  contents: write
+  pull-requests: write
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout project
+        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
+
+      - name: Install Earthly
+        uses: earthly/actions-setup@v1
+        with:
+          github-token: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Build image
+        run: earthly --secret GITHUB_TOKEN +mkdocs-build
+        env:
+          EARTHLY_CI: true
+          EARTHLY_PUSH: "${{ github.event_name == 'push' }}"
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/Earthfile b/Earthfile
index 2b6d89a..0ef8ed3 100644
--- a/Earthfile
+++ b/Earthfile
@@ -157,3 +157,22 @@
   WORKDIR /src
   COPY . /src
   RUN --secret GITHUB_TOKEN go run ./cmd/atmosphere-ci image repo sync ${project}
+
+mkdocs-image:
+  FROM ghcr.io/squidfunk/mkdocs-material:9.5.4
+  RUN pip install \
+    mkdocs-literate-nav
+  SAVE IMAGE mkdocs
+
+mkdocs-serve:
+  LOCALLY
+  WITH DOCKER --load=+mkdocs-image
+    RUN docker run --rm -p 8000:8000 -v ${PWD}:/docs mkdocs
+  END
+
+mkdocs-build:
+  FROM +mkdocs-image
+  COPY . /docs
+  RUN mkdocs build
+  RUN --push --secret GITHUB_TOKEN git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/vexxhost/magnum-cluster-api.git
+  RUN --push mkdocs gh-deploy --force
diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md
new file mode 100644
index 0000000..7377fb4
--- /dev/null
+++ b/docs/SUMMARY.md
@@ -0,0 +1,13 @@
+- [Deployment Guide](deploy.md)
+- [Ingress](ingress.md)
+- [Monitoring](monitoring.md)
+- OpenStack
+    - [Cinder](openstack/cinder.md)
+    - [Neutron](openstack/neutron.md)
+    - [Nova](openstack/nova.md)
+    - [Octavia](openstack/octavia.md)
+- Developer Guide
+    - [Getting Started](developer/getting-started.md)
+    - [Services](developer/services.md)
+    - [Images](developer/images.md)
+    - [Testing](developer/testing.md)
diff --git a/docs/admin/monitoring.md b/docs/admin/monitoring.md
deleted file mode 100644
index 02e18b2..0000000
--- a/docs/admin/monitoring.md
+++ /dev/null
@@ -1,34 +0,0 @@
-# Monitoring
-
-## Creating silences
-
-In order to create a silence, you'll need to login to your Grafana instance that
-is deployed as part of Atmosphere as an admin user.
-
-1. Click on the hamburger menu in the top left corner and select "Alerting"
-   and then "Silences" from the menu.
-
-   ![Silences menu](static/monitoring-silences-menu.png)
-
-2. Ensure that you select "AlertManager" on the top right corner of the page,
-   this will make sure that you create a silence inside of the AlertManager
-   that is managed by the Prometheus operator instead of the built-in Grafana
-   AlertManager which is not used.
-
-   ![AlertManager list](static/monitoring-alertmanger-list.png)
-
-   !!! warning
-
-   It's important that you select the AlertManager that is managed by the
-   Prometheus operator, otherwise your silence will not be applied to the
-   Prometheus instance that is deployed as part of Atmosphere.
-
-3. Click the "Add Silence" button and use the AlertManager format to create
-   your silence, which you can test by seeing if it matches any alerts in the
-   list labeled "Affected alert instances".
-
-!!! note
-
-    It is strongly recommended that you create a silence with the least amount
-    of needed labels which will make sure that small minor changes to the
-    alerts will not break your silence.
diff --git a/docs/admin/deploy.md b/docs/deploy.md
similarity index 100%
rename from docs/admin/deploy.md
rename to docs/deploy.md
diff --git a/docs/admin/ingress.md b/docs/ingress.md
similarity index 94%
rename from docs/admin/ingress.md
rename to docs/ingress.md
index e7470da..4378cda 100644
--- a/docs/admin/ingress.md
+++ b/docs/ingress.md
@@ -6,8 +6,8 @@
 
 !!! warning
 
-   If you make any changes to the ingress configuration, you may see a small
-   outage as the ingress controller is restarted.
+    If you make any changes to the ingress configuration, you may see a small
+    outage as the ingress controller is restarted.
 
 ## Customization
 
diff --git a/docs/monitoring.md b/docs/monitoring.md
new file mode 100644
index 0000000..203d0c6
--- /dev/null
+++ b/docs/monitoring.md
@@ -0,0 +1,71 @@
+# Monitoring
+
+## Creating silences
+
+In order to create a silence, you'll need to login to your Grafana instance that
+is deployed as part of Atmosphere as an admin user.
+
+1. Click on the hamburger menu in the top left corner and select "Alerting"
+   and then "Silences" from the menu.
+
+   ![Silences menu](static/monitoring-silences-menu.png)
+
+2. Ensure that you select "AlertManager" on the top right corner of the page,
+   this will make sure that you create a silence inside of the AlertManager
+   that is managed by the Prometheus operator instead of the built-in Grafana
+   AlertManager which is not used.
+
+   ![AlertManager list](static/monitoring-alertmanger-list.png)
+
+   !!! warning
+
+   It's important that you select the AlertManager that is managed by the
+   Prometheus operator, otherwise your silence will not be applied to the
+   Prometheus instance that is deployed as part of Atmosphere.
+
+3. Click the "Add Silence" button and use the AlertManager format to create
+   your silence, which you can test by seeing if it matches any alerts in the
+   list labeled "Affected alert instances".
+
+!!! note
+
+    It is strongly recommended that you create a silence with the least amount
+    of needed labels which will make sure that small minor changes to the
+    alerts will not break your silence.
+
+## Persistence
+
+For Grafana, rather than enabling persistence through the application's user
+interface or manual Helm chart modifications, dashboards should be managed
+directly via the Helm chart values.
+
+!!! warning
+
+    It is important to avoid manual persistence configurations, especially for
+    services like Grafana, where dashboards and data sources can be saved. Such
+    practices are not captured in version control and pose a risk of data loss,
+    configuration drift, and upgrade complications.
+
+To manage Grafana dashboards through Helm, you can include the dashboard
+definitions within your configuration file. By doing so, you facilitate
+version-controlled dashboard configurations that can be replicated across
+different deployments without manual intervention.
+
+For example, a dashboard can be defined in the Helm values like this:
+
+```yaml
+kube_prometheus_stack_helm_values:
+  grafana:
+    dashboards:
+      default:
+        my-dashboard:
+          gnetId: 10000
+          revision: 1
+          datasource: Prometheus
+```
+
+This instructs Helm to fetch and configure the specified dashboard from
+[Grafana.com](https://grafana.com/grafana/dashboards/), using Prometheus as the data source.
+
+You can find more examples of how to do this in the Grafana Helm chart
+[Documentation](https://github.com/grafana/helm-charts/tree/main/charts/grafana#import-dashboards).
diff --git a/docs/storage.md b/docs/openstack/cinder.md
similarity index 98%
rename from docs/storage.md
rename to docs/openstack/cinder.md
index d06b252..50abdf7 100644
--- a/docs/storage.md
+++ b/docs/openstack/cinder.md
@@ -1,4 +1,4 @@
-# Storage
+# Cinder
 
 ## Built-in Ceph cluster
 
diff --git a/docs/user/networking.md b/docs/openstack/neutron.md
similarity index 98%
rename from docs/user/networking.md
rename to docs/openstack/neutron.md
index ddbfa91..f853970 100644
--- a/docs/user/networking.md
+++ b/docs/openstack/neutron.md
@@ -1,4 +1,4 @@
-# Networking
+# Neutron
 
 ## Hardware Acceleration
 
diff --git a/docs/admin/compute.md b/docs/openstack/nova.md
similarity index 97%
rename from docs/admin/compute.md
rename to docs/openstack/nova.md
index 3b3d4b0..a94b71f 100644
--- a/docs/admin/compute.md
+++ b/docs/openstack/nova.md
@@ -1,4 +1,4 @@
-# Compute Service
+# Nova
 
 ## Troubleshooting
 
diff --git a/docs/operator.md b/docs/openstack/octavia.md
similarity index 86%
rename from docs/operator.md
rename to docs/openstack/octavia.md
index a9a8c90..0b3b8fe 100644
--- a/docs/operator.md
+++ b/docs/openstack/octavia.md
@@ -1,8 +1,6 @@
-# Operator Documentation
+# Octavia
 
-## Octavia
-
-### Accessing Amphorae
+## Accessing Amphorae
 
 Atmosphere configures an SSH keypair which allows you to login to the Amphorae
 for debugging purposes.  The `octavia-worker` containers are fully configured
diff --git a/docs/admin/static/monitoring-alertmanger-list.png b/docs/static/monitoring-alertmanger-list.png
similarity index 100%
rename from docs/admin/static/monitoring-alertmanger-list.png
rename to docs/static/monitoring-alertmanger-list.png
Binary files differ
diff --git a/docs/admin/static/monitoring-silences-menu.png b/docs/static/monitoring-silences-menu.png
similarity index 100%
rename from docs/admin/static/monitoring-silences-menu.png
rename to docs/static/monitoring-silences-menu.png
Binary files differ
diff --git a/docs/user/sso.md b/docs/user/sso.md
deleted file mode 100644
index f2e9a45..0000000
--- a/docs/user/sso.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# SSO using Keycloak

-

-You can enable Keycloak to be the primary source of truth for authentication for the cloud and all the different components, such as OpenStack dashboard, Grafana, etc.

-

-```yaml

-keycloak_host:

-```

diff --git a/mkdocs.yml b/mkdocs.yml
index 3bf826e..472c06a 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -1,7 +1,17 @@
 site_name: Atmosphere
 theme:
   name: material
+  features:
+    - content.tabs.link
+    - navigation.tracking
 markdown_extensions:
   - admonition
+  - def_list
   - pymdownx.details
   - pymdownx.superfences
+  - pymdownx.tabbed:
+      alternate_style: true
+plugins:
+  - literate-nav:
+      nav_file: SUMMARY.md
+copyright: Atmosphere is a community effort led by <a href="https://vexxhost.com">VEXXHOST, Inc.</a>