When developing locally, you often need different settings than production (URL, analytics, Disqus, etc.).
Problem description:
We want local, staging, and production Jekyll builds to behave differently without duplicating the entire configuration file.
What we are solving actually:
We are solving configuration layering and environment safety. The risk is not only inconvenience; it is accidentally running production analytics, wrong URLs, or deployment-specific settings in local builds.
What we are doing actually:
- Keep shared defaults in
_config.yml. - Put environment-specific overrides in separate config files.
- Use
JEKYLL_ENVto control template behavior such as analytics and ads.
flowchart LR
A[_config.yml] --> D[Effective Config]
B[_config-dev.yml] --> D
C[JEKYLL_ENV] --> D
D --> E[Local / staging / production build]
Typical Local vs Production Difference
# local
url: http://localhost:4000
google_analytics:
disqus_user:
# production
url: http://sandeepbhardwaj.github.io
google_analytics: UA-XXXXXX-X
disqus_user: your-disqus-id
Option 1: Separate Config File
Create _config-dev.yml and run:
jekyll serve --watch --config _config-dev.yml
Option 2: Override the Base Config
Keep base config and override only changed values:
jekyll serve --watch --config _config.yml,_config-dev.yml
Example _config-dev.yml:
url: http://localhost:4000
google_analytics:
disqus_user:
Environment Variable Pattern (JEKYLL_ENV)
Jekyll exposes JEKYLL_ENV so templates can toggle behavior by environment.
Run local development:
JEKYLL_ENV=development jekyll serve --watch --config _config.yml,_config-dev.yml
Run production build:
JEKYLL_ENV=production jekyll build --config _config.yml,_config-prod.yml
In templates:
<!-- analytics script -->
This keeps analytics/ads disabled locally while enabling them in production.
Practical Multi-Environment Setup
A clean structure for growing sites:
_config.yml: shared defaults_config-dev.yml: localhost URL, debug flags, no analytics_config-staging.yml: staging URL and test integrations_config-prod.yml: final URL + production identifiers
Example staging serve:
JEKYLL_ENV=staging jekyll serve --config _config.yml,_config-staging.yml
Common Pitfalls
- Overriding too many keys in env files (hard to reason about final config).
- Hardcoding production URLs inside markdown instead of using
site.urlandsite.baseurl. - Running production build without
JEKYLL_ENV=production. - Forgetting that later config files override earlier ones in
--config.
Debug Tip: Inspect Effective Config
To verify what Jekyll is actually using:
JEKYLL_ENV=development jekyll build --config _config.yml,_config-dev.yml --verbose
Use this when local behavior differs from CI or GitHub Pages builds.
Recommendation
Use option 2 for cleaner maintenance. Keep shared defaults in _config.yml and environment-specific overrides in _config-dev.yml.
Debug steps:
- build with
--verbosewhen the effective config does not match expectations - keep override files minimal so it is obvious what changes by environment
- verify production-only template blocks are gated by
jekyll.environment - avoid hardcoding production URLs in markdown or includes
Key Takeaways
- Separate defaults from environment overrides.
- Keep local and production behavior deterministic.
- Use minimal override files to reduce maintenance overhead.
- Use
JEKYLL_ENV+ config layering for reliable deploy pipelines.
Practical Checkpoint
A short but valuable final check for jekyll environment variables and multiple config files is to write down the one misuse pattern most likely to appear during maintenance. That small note makes the article more useful when someone revisits it months later under pressure.
Comments