NNicheStackTutorials
← All tutorials
Hugo

Hugo Environment Variables on Vercel (Build & Runtime)

HUGO_BASEURL, HUGO_VERSION, production flags, and injecting analytics IDs without committing secrets.

·10 min read·Hugo
Hugo Environment Variables on Vercel (Build & Runtime) cover

Tested on: Vercel, Hugo 0.139.4, preview + production environments Goal: One repo, correct URLs per environment, Plausible domain injected at build.

Hugo env vars Vercel understands

VariablePurpose
HUGO_VERSIONPin Hugo binary (use 0.139.4-extended for SCSS)
HUGO_BASEURLOverrides baseURL in hugo.toml
HUGO_ENVSets .Site.IsProduction / HUGO_ENV=production
HUGO_ENABLEGITINFOtrue for lastmod from git

Set in Vercel → Settings → Environment Variables. Scope: Production, Preview, Development separately.

Production vs preview baseURL

hugo.toml default:

baseURL = "https://myblog.com/"

Preview deployments need dynamic URL. Vercel provides VERCEL_URL (no scheme).

package.json build script:

{
  "scripts": {
    "build": "node scripts/hugo-build.mjs"
  }
}

scripts/hugo-build.mjs:

import { execSync } from 'node:child_process';

const prod = process.env.VERCEL_ENV === 'production';
const base = prod
  ? 'https://myblog.com/'
  : `https://${process.env.VERCEL_URL}/`;

process.env.HUGO_BASEURL = base;
process.env.HUGO_ENV = prod ? 'production' : 'development';

console.log('HUGO_BASEURL=', base);
execSync('hugo --gc --minify', { stdio: 'inherit' });

Vercel build command: npm run build

Inject analytics without hardcoding

hugo.toml:

[params]
  plausibleDomain = ""

Override via env — Hugo doesn't read arbitrary env into params natively. Use:

config/production/params.toml not ideal on Vercel.

Template approach in layouts/partials/analytics.html:

{{- $domain := getenv "PLAUSIBLE_DOMAIN" -}}
{{- if and hugo.IsProduction $domain -}}
<script defer data-domain="{{ $domain }}" src="https://plausible.io/js/script.js"></script>
{{- end -}}

Vercel env:

PLAUSIBLE_DOMAIN = myblog.com

Only for Production scope.

Conditional robots noindex on preview

{{- if ne hugo.Environment "production" -}}
<meta name="robots" content="noindex, nofollow">
{{- end -}}

Set HUGO_ENV=development on preview via build script.

Errors

canonical URL points to localhost:1313

HUGO_BASEURL unset on preview. Script above fixes.

getenv "PLAUSIBLE_DOMAIN" returns empty in local hugo server

Expected. Export locally:

export PLAUSIBLE_DOMAIN=localhost
hugo server
VERCEL_URL undefined in local vercel build

vercel dev sets it. Plain hugo server uses hugo.toml baseURL.

vercel.json

{
  "buildCommand": "npm run build",
  "outputDirectory": "public",
  "framework": null
}

Verify

Preview deploy log should show:

HUGO_BASEURL= https://myblog-abc123.vercel.app/

View source on preview → canonical uses preview URL → robots noindex.

Production:

curl -s https://myblog.com/ | grep canonical
# https://myblog.com/

Security

Never commit .env with tokens. Vercel env for Production only. Preview uses noindex — safe for draft content.

Tags: HugoVercelEnvironment VariablesConfig