Alright, let’s get our hands dirty with the file that tells Hugo how to behave: the configuration file. You’ll find this bad boy at the root of your project, named hugo.toml, hugo.yaml, or hugo.json. The format is your call; I’m a TOML person because it strikes a nice balance between being human-readable and not needing to care about indentation like YAML. Hugo doesn’t play favorites; it’ll use whichever one it finds first, in that order.

This file is the mission control for your site. It’s where you set global site-wide parameters, configure your build, tell Hugo where to find things, and generally boss it around. Think of it as the rulebook before the game starts.

The Absolute Basics: What You Must Set

Open up your config file. If it’s blank, or if you’re starting from scratch, here are the non-negotiable settings. These are the ones Hugo will very politely complain about if they’re missing.

baseURL = "https://your-awesome-site.com/"
languageCode = "en-us"
title = "My Incredible Hugo Site"

The baseURL is the absolute root of your site. Get this wrong, and your builds will be a mess of broken links. For local development, it’s fine to set it to "http://localhost:1313/", but for your production build, this must be the final, full URL. languageCode is a fallback for if you’re not using i18n (multi-language setups), and title is, well, your site’s title. Shocking, I know.

The Fun Stuff: Configuring Your Build

This is where you start to really tell Hugo what to do. The config file lets you enable/disable features, set build options, and control your output.

# TOML Example
enableRobotsTXT = true
buildDrafts = false
buildFuture = false
buildExpired = false
enableEmoji = true
pygmentsUseClasses = true # This is crucial for styling code blocks

A quick pro-tip: never set buildDrafts = true in your production config file. You will one day accidentally deploy a post that says “THIS IS A TEST DO NOT PUBLISH” to the entire world. I’ve seen it happen. Instead, use the --buildDrafts flag when running hugo server locally. The config file is for the default, safe state.

Theming: Telling Hugo Where to Look

You’ve probably got a theme. Hugo needs to know about it. The theme directive can be a string for one theme, or an array if you’re doing something fancy with theme components.

theme = ["my-shiny-theme", "some-theme-component"]

Why an array? It allows you to layer themes and components. Hugo will look for templates and files in the reverse order of that array. So it checks some-theme-component first, then my-shiny-theme, and finally your project’s own layouts/ directory. This last-one-wins precedence is your superpower for overriding theme files without ever touching the theme’s code itself. Just create a file with the same relative path in your layouts/ folder.

Custom Parameters: Your Playground

The [params] section is your personal junk drawer. This is where you define custom site-wide variables that you can access anywhere in your templates with .Site.Params. This is incredibly powerful for managing content that isn’t a page or a post.

[params]
  author = "Your Name Here"
  description = "A site about awesome stuff"
  github_username = "yourname"
  since = 2023 # You can use numbers...
  showSidebar = true # ...or booleans...

  [params.social]
    twitter = "https://twitter.com/yourname" # ...or nested tables!
    linkedin = "https://linkedin.com/in/yourname"

The sheer flexibility here is a blessing and a curse. You can organize things logically, but it’s also easy to create a sprawling, messy config. My advice? Keep it structured and document it within the config file itself with comments. Future-you will be grateful.

The Dark Art of Output Formats

Hugo can generate more than just HTML. Think RSS, JSON, AMP pages, or even custom formats you define. This is configured under the [outputs] section. The designers made a choice here that still baffles me: the home page (home), regular pages (page), and section lists (section) are all configured separately.

[outputs]
  home = ["HTML", "RSS"]
  page = ["HTML"]
  section = ["HTML", "RSS"]

Why is this absurd? Because 99% of users will never need this level of granularity, and it just adds cognitive overhead. The good news? If you don’t define this section at all, Hugo sensibly defaults to ["HTML"] for everything plus ["RSS"] for the home page. So you can often just ignore this entirely unless you’re doing something truly exotic. It’s a classic case of power that’s there if you need it, but feels like a weird complication if you don’t.

The config file is the first thing Hugo reads, and it sets the stage for everything. Get comfortable here. Tweak things. Break things locally. It’s the best way to learn exactly how this brilliant, occasionally quirky, static site generator works.