Hugo Environment-Specific Builds: How Production and Development Modes Work
Hugo handles environment-specific builds through a hierarchical configuration system that checks the --environment CLI flag, HUGO_ENVIRONMENT environment variable, or environment config key, defaulting to "development" when none are specified.
The gohugoio/hugo static site generator treats the build environment as a first-class configuration value that influences cascade rules, template rendering, and asset pipeline behavior. Understanding how Hugo distinguishes between production and development environments allows developers to conditionally enable features like minification, fingerprinting, and debugging across different deployment contexts.
Setting the Build Environment
Hugo accepts the target environment through three distinct input methods, evaluated in a specific priority order during the build initialization phase.
Configuration File
Define the environment explicitly in your site configuration using the environment key. In config.yaml or config.toml:
environment: production
The value is read by cfg.Environment() from the loaded configuration and stored in config.AllProvider.Environment(), as defined in [config/configProvider.go](https://github.com/gohugoio/hugo/blob/master/config/configProvider.go#L34-L38).
Environment Variables
Set HUGO_ENVIRONMENT or the shorthand HUGO_ENV before invoking the Hugo binary:
export HUGO_ENVIRONMENT=production
hugo
These variables are processed during command execution and injected into external tool environments via GetExecEnviron in [common/hugo/hugo.go](https://github.com/gohugoio/hugo/blob/master/common/hugo/hugo.go#L102-L115).
CLI Flag Override
The --environment (or -e) flag takes highest precedence and overrides all other sources:
hugo server --environment=production
This flag is parsed in [commands/commandeer.go](https://github.com/gohugoio/hugo/blob/master/commands/commandeer.go#L546-L550) and stored in c.r.environment, then injected into the configuration during build setup in [commands/hugobuilder.go](https://github.com/gohugoio/hugo/blob/master/commands/hugobuilder.go#L1084-L1100).
Default Fallback
When no environment is specified via flag, variable, or config, Hugo falls back to the constant EnvironmentDevelopment ("development"). The production equivalent is defined as EnvironmentProduction ("production"), both declared in [common/hugo/hugo.go](https://github.com/gohugoio/hugo/blob/master/common/hugo/hugo.go#L45-L48).
Runtime Environment Usage
Once determined, the environment value propagates through multiple subsystems to enable conditional behavior.
Cascade Rules and Page Matching
The PageMatcher logic in [resources/page/page_matcher.go](https://github.com/gohugoio/hugo/blob/master/resources/page/page_matcher.go#L55-L97) evaluates environment glob patterns when applying cascade front-matter rules. The matcher checks:
if m.Environment != "" {
g, err := hglob.GetGlob(m.Environment)
if err == nil && !g.Match(environment) { return false }
}
This allows configuration like:
cascade:
- target:
environment: production
minify: true
Template Access
Templates retrieve the current environment through the global Hugo object:
{{ .Site.Hugo.Environment }} // Returns "development" or "production"
This method is implemented in [resources/page/hugoinfo.go](https://github.com/gohugoio/hugo/blob/master/resources/page/hugoinfo.go#L59-L64), where HugoInfo.Environment() forwards to the underlying configuration provider.
Asset Pipeline Integration
External build tools such as PostCSS and Babel receive the environment via GetExecEnviron, which explicitly sets HUGO_ENVIRONMENT and HUGO_ENV in the process environment. This allows asset pipelines to switch behavior based on the build mode without additional configuration.
Server Confirmation
When running hugo server, the active environment is printed to standard output for confirmation, as implemented in [commands/server.go](https://github.com/gohugoio/hugo/blob/master/commands/server.go#L249-L250).
Environment Resolution Flow
The resolution process follows a strict hierarchy during site initialization:
- Command Parsing –
commands/hugobuilder.goevaluates the--environmentflag first, falling back to environment variables and configuration file values. - Configuration Loading –
config.LoadConfigFromDirreceives the final environment string and stores it in theAllProviderimplementation. - Runtime Propagation – The value becomes accessible via:
.Site.Hugo().Environment()for templatesPageMatcher.Environmentfor cascade matchingGetExecEnvironfor external tool execution
Practical Implementation Examples
Switching Analytics in Templates
Use the environment to conditionally load production-only analytics scripts:
{{ if eq .Site.Hugo.Environment "production" }}
<script async src="https://analytics.example.com/script.js"></script>
{{ end }}
Production-Only Minification
Apply minification exclusively to production builds using cascade rules:
cascade:
- target:
environment: production
build:
minify: true
External Tool Configuration
Configure PostCSS to enable purgecss only in production by checking process.env.HUGO_ENVIRONMENT:
// postcss.config.js
const purgecss = require('@fullhuman/postcss-purgecss');
module.exports = {
plugins: [
...(process.env.HUGO_ENVIRONMENT === 'production' ? [purgecss()] : [])
]
};
Summary
- Hugo accepts environment configuration via the
--environmentCLI flag,HUGO_ENVIRONMENTenvironment variable, orenvironmentconfig key, falling back to"development"by default. - The constants
EnvironmentProductionandEnvironmentDevelopmentincommon/hugo/hugo.godefine the canonical string values. commands/hugobuilder.goorchestrates the environment resolution during build initialization.- Cascade rules in
resources/page/page_matcher.gouse glob matching to apply environment-specific front matter. - Templates access the environment via
.Site.Hugo.Environment, which retrieves the value from the configuration provider. - External asset pipelines receive the environment through
HUGO_ENVIRONMENTandHUGO_ENVvariables injected byGetExecEnviron.
Frequently Asked Questions
How do I check the current Hugo environment in a template?
Access the environment through the global Hugo object using {{ .Site.Hugo.Environment }}. This returns the string "development" or "production" (or any custom value you set) and is implemented in resources/page/hugoinfo.go as a wrapper around the configuration provider's Environment() method.
What is the priority order for Hugo environment configuration?
Hugo evaluates sources in the following priority: first the --environment CLI flag, then the HUGO_ENVIRONMENT or HUGO_ENV environment variables, then the environment key in your configuration file, and finally defaults to "development" if none are provided. This hierarchy is enforced in commands/hugobuilder.go during the build initialization phase.
Can I use custom environment names beyond production and development?
Yes, while Hugo defines EnvironmentProduction and EnvironmentDevelopment constants in common/hugo/hugo.go, you can specify any arbitrary string as the environment value. The PageMatcher in resources/page/page_matcher.go uses glob patterns, allowing you to target specific environments or groups (e.g., staging) in your cascade rules and conditional template logic.
How does Hugo pass the environment to PostCSS and other external tools?
Hugo injects the current environment into external process environments through the GetExecEnviron function in common/hugo/hugo.go. This function explicitly sets both HUGO_ENVIRONMENT and HUGO_ENV variables before spawning tools like PostCSS or Babel, allowing these pipelines to branch their behavior based on the build mode without requiring separate configuration files.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →