···11diff --git c/docs/Getting-Started.md w/docs/Getting-Started.md
22-index a8ce6f4..61d5c0d 100644
22+index 2effedc..7a864a6 100644
33--- c/docs/Getting-Started.md
44+++ w/docs/Getting-Started.md
55-@@ -119,6 +119,7 @@ VoidAuth is configurable primarily by environment variable. The available enviro
55+@@ -125,6 +125,7 @@ VoidAuth is configurable primarily by environment variable. The available enviro
66 | APP_COLOR | `#906bc7` | Theme color, rgb format; ex. #xxyyzz | | ✅ |
77 | APP_FONT | `monospace` | Font used in the web interface and sent emails. Safe fonts should be used, if a font is missing it will fallback to default. Multiple font families may be chosen in fallback-font format. ex. `APP_FONT: "Tahoma, Verdana, sans-serif"` | | |
88 | CONTACT_EMAIL | | The email address used for 'Contact' links, which are shown on most end-user pages if this is set. | | |
···10101111 #### Database Settings
1212 When using the `sqlite` database adapter type, no additional database connection variables are required. You will need a mounted volume to hold the generated `db.sqlite` file, as shown in the SQLite docker compose example above.
1313-@@ -189,4 +190,3 @@ For information on how to change the email templates used for invitations, passw
1313+@@ -198,4 +199,3 @@ For information on how to change the email templates used for invitations, passw
14141515 ### Multi-Domain Protection
1616 You can secure multiple domains you own by running multiple instances of VoidAuth using the same database. They should have the same **STORAGE_KEY** and **DB_\*** variables, but may otherwise have completely different configurations. The **APP_URL** variables of each would cover a different domain. If the domains you were trying to secure were `example.com` and `your-domain.net` you might set the **APP_URL** variables like `https://auth.example.com` and `https://id.your-domain.net`. These two instances would share everything in the shared DB, including users, OIDC Apps, ProxyAuth Domains, etc.
1717-
1818diff --git c/server/cli/server.ts w/server/cli/server.ts
1919-index bfd6e25..9d7b676 100644
1919+index f076cf2..39253b0 100644
2020--- c/server/cli/server.ts
2121+++ w/server/cli/server.ts
2222-@@ -18,9 +18,6 @@ import { createInitialAdmin } from '../db/user'
2323- import { logger } from '../util/logger'
2424- import { standardRateLimit } from '../util/rateLimit'
2222+@@ -20,7 +20,9 @@ import { sensitiveRateLimit, standardRateLimit } from '../util/rateLimit'
2323+ import { FORBIDDEN_PATHS, NOT_FOUND_PATHS } from '@shared/constants'
25242626--const PROCESS_ROOT = path.dirname(process.argv[1] ?? '.')
2525+ const PROCESS_ROOT = path.dirname(process.argv[1] ?? '.')
2726-const FE_ROOT = path.join(PROCESS_ROOT, '../frontend/dist/browser')
2828--
2727++const FE_ROOT = process.env.FRONTEND_PATH
2828++ ? path.resolve(process.env.FRONTEND_PATH)
2929++ : path.join(PROCESS_ROOT, '../frontend/dist/browser')
3030+2931 export async function serve() {
3032 // Do not wait for theme to generate before starting
3131- void generateTheme()
3232-@@ -138,7 +135,7 @@ export async function serve() {
3333- })
3434-3535- // frontend
3636-- app.use(`${basePath()}/`, express.static(FE_ROOT, {
3737-+ app.use(`${basePath()}/`, express.static(appConfig.FRONTEND_PATH, {
3838- index: false,
3939- fallthrough: true,
4040- }))
4141-@@ -172,7 +169,8 @@ export async function serve() {
3333+@@ -217,7 +219,7 @@ export async function serve() {
3434+ }
3535+ next()
3636+ },
3737+- express.static(FE_ROOT, {
3838++ express.static(appConfig.FRONTEND_PATH, {
3939+ index: false,
4040+ fallthrough: true,
4141+ }),
4242+@@ -272,7 +274,8 @@ export async function serve() {
42434344 function modifyIndex() {
4444- // add APP_TITLE
4545+ // add APP_TITLE
4546- let index = fs.readFileSync(path.join(FE_ROOT, './index.html')).toString().replace('<title>', '<title>' + appConfig.APP_TITLE)
4647+ const indexPath = path.join(appConfig.FRONTEND_PATH, './index.html')
4748+ let index = fs.readFileSync(indexPath).toString().replace('<title>', '<title>' + appConfig.APP_TITLE)
···4950 // Replace base href with path of APP_URL
5051 index = index.replace(/<base[^>]*href=[^>]*>/g, `<base href="${basePath()}/"/>`)
5152diff --git c/server/util/config.ts w/server/util/config.ts
5252-index 1e43667..e9f7efa 100644
5353+index 16a3a57..7801278 100644
5354--- c/server/util/config.ts
5455+++ w/server/util/config.ts
5555-@@ -7,12 +7,16 @@ import type { ClientResponse } from '@shared/api-response/ClientResponse.js'
5656- import Docker from 'dockerode'
5757- import { clientUpsertValidator } from '@shared/api-request/admin/ClientUpsert'
5656+@@ -8,12 +8,16 @@ import { clientUpsertValidator } from '@shared/api-request/admin/ClientUpsert'
5857 import zod from 'zod'
5858+ import type { SecureVersion } from 'node:tls'
5959+ import { randomBytes } from 'node:crypto'
5960+import path from 'node:path'
6061+
6162+const PROCESS_ROOT = path.dirname(process.argv[1] ?? '.')
+3-3
packages/voidauth/package.nix
···99}:
1010buildNpmPackage (finalAttrs: {
1111 pname = "voidauth";
1212- version = "1.11.2";
1212+ version = "1.12.2";
13131414 src = fetchFromGitHub {
1515 owner = "voidauth";
1616 repo = "voidauth";
1717 tag = "v${finalAttrs.version}";
1818- hash = "sha256-KMKwg2V/epw5oKhcboWH3xcEZbjvA86A3kv5pooa2HE=";
1818+ hash = "sha256-JbxwlgKldI6UoZ5PSX3nfiHsWqeVbMS7wyC7QKdbdpk=";
1919 };
20202121 # Override frontend path with an environment variable so we don't need to copy it around
···4848 echo '!dist/index.mjs' >> .npmignore
4949 '';
50505151- npmDepsHash = "sha256-AWIy/DeStXoTHJv8Sk/Sqz0xJugOVg+zLq5TUv2PJ9U=";
5151+ npmDepsHash = "sha256-36HGJ8Kg8kRg9n0q1MHVEyaJ+VCXcMecqsLk+Wgw0yc=";
5252 npmBuildScript = "server:build";
53535454 postInstall = ''