HUGO

Vercel Tips

I migrated a couple sites to Vercel (previously called Zeit), because they have a point of presence on their CDN in Tokyo where I am (whereas Netlify does not, if you are not paying for “enterprise”), which makes them a bit snappier compared to Netlify for me.

They’re the people who build next.js, so it’s built with node in mind, but it works well for Hugo, but defaults to building with an old version. I forked Bjørn Erik’s GitHub - bep/hugo-starter-tailwind-basic: A basic and simple to set up Hugo with TailwindCSS starter project. and it worked out of the box on Vercel, as long as I specified the Hugo version, which you can do in two ways:

  1. make an environment variable in your Vercel dashboard, under the project, in settings - e.g.: HUGO_VERSION = 0.88.1.
  2. add a vercel.json in the root of your repo, and add this, adjusting the version as needed:
{
  "build": {
    "env": {
    "HUGO_VERSION": "0.88.1"
    }
  }
}

I like having the vercel.json because it’s right there and easy to change. The settings environment variable takes precedence, if it exists.

Vercel has a vercel command which you can install via brew on Mac, and you run that to initialize a project. It adds a .vercel folder to specify the account and project id’s, and pushes your project to Vercel, prompting for some various settings. It failed on one of my repo’s saying it could not find git, which I did not suss out. I just went to the Vercel GUI, connected the project to the connected Github project, and then a push to master / main triggered the deploy.

If Vercel autodetects your project is Hugo, it will set a default Hugo build command, but you can override that in project settings. I have a project that uses the sass flavor of Tachyons CSS, which needs a git clone of the source files before it can build using toCSS in the head partial.

...
{{ $styles := resources.Get "rc-main.scss" | resources.ExecuteAsTemplate "style.scss" . | toCSS | postCSS | minify | fingerprint }}
<link rel="stylesheet" href="{{ $styles.Permalink }}" integrity="{{ $styles.Data.Integrity }}" />
...

There’s a place to put the build command in your Vercel project settings, and you can set it to “override” and enter the command you want to use. I used something like:

echo "=== prepping for ACME ===" && ls && cd assets && git clone https://github.com/tachyons-css/tachyons-sass && cd .. && echo "=== build it ===" && hugo --gc --minify --ignoreCache --verbose

That took a bit of trial and error, but you can add typical bash-y commands in there separated by the && (if the previous succeeds, proceed to the next). The echo commands make the output a little easier to find in the log.

A little aesthetic thing: there’s no native Readme.md status badge you can add like Netlify provides, but this repo generates badges for Vercel projects that look like shields.io ones. For example if your project name in Vercel is acme-inc-site, then you can add this to the top of your README.md to get a badge:

![Vercel](https://therealsujitk-vercel-badge.vercel.app/?app=acme-inc-site&style=for-the-badge)

Vercel apparently just auto detects and builds your package.json, which then might use something like postcss.config.js or tailwind.config.js. I did not analyze it deeply, but @bep’s repo “just worked”, which was a pleasant surprise.

Vercel creates domains like acme-inc-site.vercel.app, and you can add your custom ones by adding DNS entries. Just add whatever domain you need, and add the DNS entries that Vercel tells you to (so far, just A or CNAME) into your DNS hosting service. You can forward domains, which you would use if forwarding www to the apex, and you can link a domain like preview.acme-inc.site to a git branch like develop. Then you just do your normal operations, pushing to master / main or develop, and it deploys on the expected URL.

A couple more things to note:

  • the free / hobby version links to personal github repos only (edit: and are for personal, non-commercial only, per the comment below from @onedrawingperday, thanks)
  • the paid version is 20 USD / mo per team-member, and links to github organization repos only
  • password protected previews are a 150 USD / month option on the paid version
  • check the Vercel docs for how to set response headers for better security on your site - they go in your vercel.json.

Hope this might help someone looking to deploy Hugo on Vercel.

Update 20210918

Ok, this is not well tested so take it as a starting point, but you can add the headers in your vercel.json like so:

{
  "build": {
    "env": {
    "HUGO_VERSION": "0.88.1"
    }
  },
  "headers": [
    {
      "source": "/(.*)",
      "headers":[
        {
          "key": "Strict-Transport-Security",
          "value": "max-age=31536000; includeSubDomains; preload"
        },
        {
          "key": "X-Frame-Options",
          "value": "SAMEORIGIN"
        },
        {
          "key": "Referrer-Policy",
          "value": "strict-origin"
        },
        {
          "key": "X-Content-Type-Options",
          "value": "nosniff"
        },
        {
          "key": "X-Powered-By",
          "value": "Hugo"
        }
      ]
    }
  ]
}

After deploying, checking the headers in any browser dev tool or via, say, curl -L -I acme-inc.site shows they are set.

Update 20210919

You can set up Hugo configs to reflect the environment, such as production vs dev (different baseURLs etc), and pass that to the hugo command:

hugo --gc --minify --ignoreCache --verbose --environment=dev

Hugo will find the config associated with “dev” in your config folder, and override what’s in _defaults:

❯ pwd
/Users/myuser/projects/acme-inc-site/config
❯ tree
.
├── _default
│   └── config.toml
├── dev
│   └── config.toml
└── master
    └── config.toml

I set up mine to mirror the branches I use in git. If you’re working in a full CLI environment, something like this will work to grab the currently checked out branch:

hugo ... --environment=$(git rev-parse --abbrev-ref HEAD)

But CI VMs have specialized environments that won’t always work exactly as your local one does. Vercel lets you expose its system environment vars to the build environment (in project settings, environment variables), so you can use this. Your build command could take advantage of the fact that Vercel sets the current git branch into VERCEL_GIT_COMMIT_REF:

echo "=== prepping for ACME ===" && ls && cd assets && git clone https://github.com/tachyons-css/tachyons-sass && cd .. && echo "=== build it ===" && hugo --gc --minify --ignoreCache --verbose --environment==$VERCEL_GIT_COMMIT_REF

This then picks up “master” for production, or “dev” for preview, so that your baseURL and other settings can be correct.

6 Likes

I just want to add that Vercel’s free Hobby Accounts are strictly for non-commercial personal use only. All commercial usage of the platform requires either a Pro or Enterprise plan.

Netlify’s free plan does not come with the above restriction, as per this discussion.

3 Likes

Fantastic writeup! I want to try Vercel but not being able to dynamically generate headers and things like you can with Netlify’s _headers and _redirects files is a bit of a killer for me.

Hopefully they can add support for it one day!

1 Like

I have been trying a few different sites on Vercel.

Several are Hugo, and you just put the vercel.json at the root of the repo, specify the “root directory” in Vercel project settings as ./ and it works. The “build command” is whatever you usually use for building to public, like hugo --gc --minify --ignoreCache --verbose. Vercel is set up to understand Hugo projects and you can select Hugo as the environment.

A couple other sites I have are simply static, hand-coded HTML, in say, an html folder under the repo. In those cases, you don’t specify a build command (click “override” and leave blank), you specify the “root directory” as html, and you have to put the vercel.json in the html folder.

The implication is, the vercel.json goes wherever you specify the “root directory”.

As for redirects, you can put them in the vercel.json.

I’m guessing that you could probably do something similar to the _headers file of Netlify, then. While it’s definitely not 1:1, it’s probably do-able I would think.

Edit to add: Vercel support sent this link to take a look at. CLI – Vercel Docs

Ah yes this is true, however I wasn’t completely clear. By dynamically generated I mean generated by Hugo itself.

I use Hugo’s templating to make my _header and _redirects files during deployment. I haven’t actually tested, but I imagine that the vercel.json is only parsed when everything first boots and not when its fully deployed?

Again, haven’t tested, so likely completely wrong!

Hmm. Looking at the build log, it’s reading the vercel.json before running hugo because it is getting the version from it. Before I set the version in vercel.json, hugo was set to something really old.

One workaround would be to simply treat it as static files, build hugo in CI and generate the vercel.json via hugo templates into the root of public, then pass that along to vercel. We know that approach works for a static set of files in a folder.

Question is, when exactly does vercel.json have to exist? Does vercel read it dynamically when pages are accessed? Or is it reading when deploying and making a cache? I’ll ask…

Yeah, talked to support and they said they don’t support a situation where the build tool builds the vercel.json on the fly. That said, I think you could take the approach of using a CI action of some kind to build hugo and vercel.json, make sure vercel.json is in public, then publish public as static to Vercel. In other words, don’t let Vercel run Hugo. Haven’t tried that, though.

Does vercel work with exampleSite? I have tried, but can’t figure it out.

I’ve tried a couple of Hugo sites on it and they work. Key points would be:

  • what build command did you tell Vercel to use, and do you have the baseURL properly specified, either in the build command or in the config?
  • what folder did you tell Vercel to look in - for a normal Hugo it should be ./ iirc
  • did you specify the version in a vercel.json at the root of the project? Or in a Vercel project environment variable?

Ok, I tried with the Hugo quickstart and it works without trouble. It took 5 min to deploy and 25 to actually write about it, lol. This repo’s readme shows my work:

Hello @RickCogley

Thanks for your reply. I think you got me wrong. I know how to deploy Hugo sites with Vercel. But what I concern about is, how to deploy Hugo with exampleSites folder. I have tried every possible way, but it failed every time.

Why don’t you make a fresh support request and explain exactly what you tried, giving a link to a repo. It might be easier to help if I understood what you’re trying.

1 Like

Excellent info!

Just FYI: although Vercel still respects setting the Hugo version in vercel.json at least for now, the current recommendation is to do it only by the first method you mentioned, using an environment variable (e.g., HUGO_VERSION). — How do I migrate away from `vercel.json` env and build.env? – Vercel Docs