Skip to content

Nuxt Better Auth template ​

Source · Demo · Database-free Nuxt template

Nuxt 4, TypeScript, Tailwind/shadcn-vue, Better Auth, PostgreSQL/Drizzle, and Brevo email, with separate staging and production deployments through DockIY.

Local development ​

Install Node.js 24+, pnpm 11+, Docker Compose, and the DockIY CLI. Encrypted files also need SOPS and your own SOPS identity. Create a project with dockiy app init nuxt-betterauth my-app, or clone the template.

bash
cp .env.example .env
openssl rand -base64 32
# Set NUXT_BETTER_AUTH_SECRET to the generated value and fill in NUXT_BREVO_*.
docker compose up -d
pnpm install --frozen-lockfile
pnpm db:migrate
pnpm dev

Nuxt and Drizzle load .env. Keep NUXT_BETTER_AUTH_URL=http://localhost:3000 locally. Email signup requires verification; configure the Brevo API key, verified sender email, and sender name for signup and password reset.

Encrypted local values ​

Configure your own .sops.yaml, then use sops edit .enc.local.env to create an encrypted file with the same keys. Wrap commands manually:

bash
sops exec-env .enc.local.env 'docker compose up -d'
sops exec-env .enc.local.env 'pnpm db:migrate'
sops exec-env .enc.local.env 'pnpm dev'

Or replace selected package.json scripts, for example:

json
"dev": "sops exec-env .enc.local.env 'nuxt dev'",
"db:migrate": "sops exec-env .enc.local.env 'drizzle-kit migrate'"

Use the underlying command inside a script to avoid recursion. SOPS injects variables without writing plaintext; restart Nuxt after changing them. Keep plaintext env files/private keys uncommitted and replace inherited encrypted files.

Staging and production ​

Start the DockIY base stack and configure your VPS connection. In dockiy.yml, set the app name and the staging/production host values. Keep each environment's secrets_file pointing at its own encrypted dotenv.

bash
sops edit .enc.staging.env
sops edit .enc.production.env

Use .env.example as the key list, with separate credentials and auth secrets:

SettingStaging exampleProduction example
POSTGRES_DB / POSTGRES_USERmy_app_stagingmy_app
POSTGRES_PASSWORDSeparate database passwordSeparate database password
POSTGRES_DOCKER_PORT54345435
NUXT_DATABASE_URLpostgresql://my_app_staging:PASSWORD@db:5432/my_app_stagingpostgresql://my_app:PASSWORD@db:5432/my_app
NUXT_BETTER_AUTH_SECRETFresh openssl rand -base64 32 outputFresh openssl rand -base64 32 output
NUXT_BETTER_AUTH_URLhttps://staging.example.comhttps://example.com
NUXT_BREVO_*API key and verified senderAPI key and verified sender
NUXT_GOOGLE_CLIENT_ID / NUXT_GOOGLE_CLIENT_SECRETOptional Google OAuth credentialsOptional Google OAuth credentials

In staging and production, the port in NUXT_DATABASE_URL is always 5432: it is PostgreSQL's internal container port. POSTGRES_DOCKER_PORT exposes a separate port on the VPS host so you can connect from your computer through an SSH tunnel.

The auth origin must match the environment host; it determines email links and OAuth callbacks. DockIY decrypts the selected file into deploy.env and passes its values to the app. New server settings need a runtimeConfig entry in nuxt.config.ts and a NUXT_* variable; only browser-visible values go under public.

Commit the application, migrations, and encrypted configuration, then deploy:

bash
dockiy app deploy staging
dockiy app deploy production --version v1.0.0

The migration image runs before the app starts. See deployment commands for status and rollback. Rollback restores the app, not the database schema.

Database ​

Keep NUXT_DATABASE_URL credentials consistent with POSTGRES_*. URL-encode special characters in URI credentials (@ → %40); keep POSTGRES_PASSWORD unencoded. Changing env values does not change credentials in an existing DB volume.

Connection fromAddress
Local Nuxt / Drizzlelocalhost:5432, or your POSTGRES_DOCKER_PORT
App, migration, or pgAdmin containerdb:5432
Desktop client via SSH tunnelYour tunnel's local port

The local example URL is postgresql://dockiy-nuxt-betterauth:secret-password@localhost:5432/dockiy-nuxt-betterauth. Changing POSTGRES_DOCKER_PORT changes only the host port; update the local URL too. Choose distinct unused ports for deployed environments; they bind to VPS loopback. To reach staging from a desktop client:

bash
ssh -N -L 15432:127.0.0.1:5434 USER@VPS
# Connect to localhost:15432 with staging credentials.

Local pgAdmin is at http://localhost:82 (admin@m.com / admin). Register host db, port 5432, with the local POSTGRES_* credentials.

Define/export tables in server/db/schema/; import getDb from #server/db for queries. After schema edits, run pnpm db:generate, review/commit the SQL and metadata, then pnpm db:migrate locally. Reserve pnpm db:push for disposable DBs.

Authentication ​

Email/password and Google auth are preconfigured.

  • Client session: import authClient from @/lib/auth-client and read the session with await authClient.useSession(useFetch). Session usage.
  • Gate pages: definePageMeta({ middleware: "auth" }). Protect pages.
  • Protect APIs separately: call const user = await requireUser(event), then check resource ownership.

Google auth setup ​

  1. Select/create a project in Google Cloud Console. Under Google Auth Platform, complete Branding and Audience; choose External for users outside your organization and add test users while testing. Consent setup.
  2. Open Clients → Create client → Web application. Add the exact callback URLs below under Authorized redirect URIs. Google OAuth setup.
  3. Copy the client ID/secret to NUXT_GOOGLE_CLIENT_ID and NUXT_GOOGLE_CLIENT_SECRET in each environment's dotenv file. Set NUXT_BETTER_AUTH_URL to the corresponding origin, then restart locally or redeploy. Better Auth Google guide.
EnvironmentAuthorized redirect URI
Localhttp://localhost:3000/api/auth/callback/google
Staginghttps://staging.example.com/api/auth/callback/google
Productionhttps://example.com/api/auth/callback/google

Use your actual domains. The callback URI must match exactly, including scheme and path; a mismatch causes redirect_uri_mismatch. Before public launch, review Audience → Publishing status and Google's requested verification steps.

Released under the MIT License.