diff --git a/README.md b/README.md
index 29633cd..12e7c02 100644
--- a/README.md
+++ b/README.md
@@ -129,3 +129,7 @@ short, it was used for:
Where AI contributed directly to a piece of code, there is a comment saying
so, for example in [frontend/app/api/types/index.ts](frontend/app/api/types/index.ts) and
[frontend/app/root.tsx](frontend/app/root.tsx).
+
+## Deployment
+
+For more details on deploying the solution on a VM, see the [deployment documentation](/docs/deployment.md).
diff --git a/docs/deployment.md b/docs/deployment.md
new file mode 100644
index 0000000..cfa5567
--- /dev/null
+++ b/docs/deployment.md
@@ -0,0 +1,167 @@
+# Deployment
+
+## Install dependencies
+
+Start by installing our dependencies:
+
+- Install [node 24](https://nodejs.org/en/download)
+- Install [pnpm](https://pnpm.io/installation)
+- Install [PostgreSQL 18](https://www.postgresql.org/download/)
+- Install [pm2](https://pm2.keymetrics.io/)
+
+The binaries provided by PostgreSQL may not be added to path automatically.
+If the commands `pg_ctl` and `initdb` fail, do the following:
+
+```sh
+cd ~
+touch .profile
+nano .profile
+# And add the following line: PATH=$PATH:/usr/lib/postgresql/18/bin/
+
+# Apply the changes
+source .profile
+```
+
+## Clone the project and set it up
+
+Clone the project:
+
+```sh
+git clone https://git.ntnu.no/IT2810-H26/T37-Project-2.git
+cd T37-Project-2
+```
+
+### Environment variables
+
+This step needs to be done only whenever environment variables are updated (`.env`).
+
+Set the correct environment values for the frontend:
+
+```sh
+cd frontend
+cp .env.example .env
+nano .env # and change the VITE_API_URL
+```
+
+And do the same for the backend:
+
+```sh
+cd backend
+cp .env.example .env
+nano .env # and change variables if needed
+```
+
+### Build the frontend
+
+Build the frontend, remove the old version and move the new content:
+
+```sh
+cd frontend
+pnpm build
+sudo rm -rf /var/www/html/project2
+sudo cp -r build/client/ /var/www/html/project2
+```
+
+> [!CAUTION]
+> We only move the `client/` folder from the build!
+> We do not use React Router's server-side functionality, because our frontend does not use server-side rendering (SSR).
+> See [react-router.config.ts](./frontend/react-router.config.ts)
+
+The move must be executed as sudo because the `/var` folders are owned by `root`.
+
+### Building the backend
+
+Build the backend like so:
+
+```sh
+cd backend
+pnpm build
+```
+
+### Prepare the database
+
+We need the seed book data file.
+For more details, please check the [backend README.md](./backend/README.md).
+
+Let's download the dataset, unzip and place it in the correct folder:
+
+```sh
+curl -L -o comprehensive-literary-greats-dataset.zip https://www.kaggle.com/api/v1/datasets/download/thedevastator/comprehensive-literary-greats-dataset
+unzip comprehensive-literary-greats-dataset.zip
+# After unzipping, a file named "books_1.Best_Books_Ever.csv" will be placed
+# in the current working directory.
+
+mv books_1.Best_Books_Ever.csv ./backend/src/db/seed/book-data.csv
+rm comprehensive-literary-greats-dataset.zip
+```
+
+Set up the database as usual:
+
+```sh
+cd backend
+pnpm db:setup
+```
+
+> [!NOTE]
+> If you receive permission errors, add your user to the `postgres` group by running the following command:
+> `sudo usermod -aG postgres $USER`
+
+## Running the project
+
+The frontend is already handling by the Apache web server by being placed in `/var/www/html/project2`.
+
+In order to run the backend, we use `pm2`.
+
+The first time project is run:
+
+```sh
+cd backend
+pm2 start --name "backend" --interpreter="/bin/bash" pnpm -- start
+```
+
+Afterwards, after changing the backend and building it, it's enough to restart it:
+
+```sh
+# This command can be done in any directory on the machine!
+pm2 restart backend
+```
+
+## Our VM setup
+
+This is what is set up on our VM (`it2810-37.idi.ntnu.no`, Ubuntu 26.04).
+Apache was already installed and running.
+
+### Installed packages
+
+```sh
+# Node 24 from NodeSource
+curl -fsSL https://deb.nodesource.com/setup_24.x | sudo bash -
+sudo apt install -y nodejs
+
+# pnpm, installed globally
+sudo npm install -g pnpm@12
+
+# PostgreSQL 18
+sudo apt install -y postgresql
+```
+
+### Apache config for client-side routes
+
+React Router handles routes like `/project2/book/5` in the browser. Without
+this config, opening such a URL directly or reloading the page gives a 404,
+because Apache looks for a file with that path. `FallbackResource` makes
+Apache serve `index.html` instead, so the app can load and show the right page.
+It only applies to the `project2` folder.
+
+```sh
+sudo tee /etc/apache2/conf-available/project2.conf > /dev/null <<'CONF'
+
+ FallbackResource /project2/index.html
+
+CONF
+sudo a2enconf project2
+sudo apache2ctl configtest # should print "Syntax OK"
+sudo systemctl reload apache2
+```
+
+This was only done once, and should only need to be run once.
diff --git a/frontend/.env.example b/frontend/.env.example
new file mode 100644
index 0000000..019061c
--- /dev/null
+++ b/frontend/.env.example
@@ -0,0 +1,2 @@
+# Must be changed to http://it2810-37.idi.ntnu.no:3001/ for production
+VITE_API_URL = http://localhost:3001
diff --git a/frontend/app/root.tsx b/frontend/app/root.tsx
index 4a24668..90cdb98 100644
--- a/frontend/app/root.tsx
+++ b/frontend/app/root.tsx
@@ -18,7 +18,9 @@ import { TooltipProvider } from "~/components/ui/Tooltip";
// Created outside the component tree so the cache survives re-renders.
const apolloClient = new ApolloClient({
- link: new HttpLink({ uri: "http://localhost:3001/" }),
+ link: new HttpLink({
+ uri: (import.meta.env.VITE_API_URL as string) || "http://localhost:3001/",
+ }),
cache: new InMemoryCache(),
});
@@ -36,21 +38,24 @@ export function Layout({ children }: { children: React.ReactNode }) {
+
-