Skip to main content
A Hugo site builds to static files: hugo build writes every page as HTML to the public folder. Sevalla publishes that folder as a static site, served from Cloudflare’s edge and free up to 100 GB of bandwidth a month. The static site build runs on Node.js and has no Hugo, so the project installs Hugo as an npm package, hugo-bin. To run the site as an application instead, build it from a Dockerfile.

At a glance

Environment variables

Before you begin

  • A Sevalla company with a payment method, and a GitHub, GitLab or Bitbucket account for the repository. See Git providers.
  • Hugo 0.160 or later and Git on your computer. See Install Hugo.
  • Node.js 24 or later on your computer, for hugo-bin.

Create the project

Create a site and add the Ananke theme as a Git submodule, as in Hugo’s quick start:
Turn on the theme and name the site in hugo.toml:
hugo.toml
Add a first post:
content/posts/welcome.md
Keep Hugo’s output and local files out of the repository:
.gitignore

Prepare the project for Sevalla

Install Hugo as a development dependency, and add a build script that runs it:
Hugo writes absolute links with baseURL from hugo.toml, which hugo new site sets to https://example.org/. On Sevalla, you set the real address in the HUGO_BASEURL environment variable, which Hugo reads instead, so the same repository builds for every domain. Run npx hugo server and open http://localhost:1313 to check the site locally. Then commit everything and push it to your Git provider:

Deploy as a static site

1

Choose the repository

In the sidebar, click Static sites, then click Create static site. Under Source, click Private repository, then choose the Git provider, the Repository and the Branch.
2

Set the build settings

Under Build settings, Sevalla fills in npm run build and dist from package.json. Hugo builds to public, so change the Publish directory:
3

Create the static site

Enter a Name under Details, then click Create static site. Sevalla starts the first deployment right away, and deploys every push to the branch while Auto-deploy is on.
4

Set the site's address

Click the Environment variables tab, then click Add environment variables. Enter HUGO_BASEURL as the Key and the site’s address as the Value, with https:// and a trailing slash, for example https://storefront-x7k2m.sevalla.page/. The domain is in the Details card on the Overview tab. Click Add environment variables.
5

Deploy again

Click the Deployments tab, click Deploy now, then click Deploy. The variable applies from this deployment on.
The build log shows the Hugo release that hugo-bin installed, such as hugo v0.169.0, then Hugo’s summary of the pages it built. npm 11 warns there that hugo-bin has an install script not yet covered by allowScripts; it runs the script anyway. See Build settings for every field.

Verify the static site

  1. On the Deployments tab, wait for the deployment’s status to change to Deployed. The site’s Status on the Overview tab changes to Live.
  2. On the Overview tab, click Visit site. The Ananke home page opens with the title Storefront and the post Welcome to Storefront, and the site title’s link points to the site’s own domain.
  3. From a terminal, with your site’s domain:
    The output is <link rel=canonical href=https://storefront-blog-x7k2m.sevalla.page/>: Hugo built the site with HUGO_BASEURL. With the API, Get static site deployment returns the deployment’s status; see Deploy with the API.
The static site's Details card with its domain, Live status, source repository and the Visit site buttonThe static site's Details card with its domain, Live status, source repository and the Visit site button
If the canonical link points to example.org, HUGO_BASEURL isn’t set for the deployment: add it and deploy again. If the deployment fails, see Troubleshoot static sites.

Deploy with a Dockerfile

Ingot, the default build strategy for applications, has no Hugo builder, so a Hugo site runs as an application only from a Dockerfile. Use one when the site needs something only applications have, such as promoting one build through the stages of a pipeline. Add a Dockerfile, a Caddyfile and a .dockerignore to the root of the repository:
Dockerfile
Caddyfile
.dockerignore
The build stage doesn’t install hugo-bin, so the site builds the same way with or without the static site setup. Sevalla clones the theme’s submodule for applications too. Commit and push the three files, then create the application:
1

Create the application

In the sidebar, click Applications, then click Create > Application. Under Source, click Private repository, then choose the Git provider, the Repository and the Branch. Under Location, choose a Data center. Keep the Size at H1 or choose a larger one, then click Create application. The application opens on its Overview tab with the status Not deployed yet: creating an application doesn’t deploy it.
2

Choose the Dockerfile build strategy

On the Settings tab, in the Build strategy section, click Update build strategy. Click Dockerfile, keep the Dockerfile path Dockerfile and the Docker context ., then click Update build strategy.
3

Set the site's address

Click the Environment variables tab, then click Add environment variables. Enter HUGO_BASEURL as the key and the application’s address as the value, with https:// and a trailing slash, for example https://storefront-x7k2m.sevalla.app/. The domain is under the application’s name in the page header. Keep Available during buildtime selected and click Add environment variables.
4

Deploy

Click the Overview tab, then click Deploy now under Recent deployments. Check the Branch and click Deploy.
The deployment’s build log shows 📦 Building and pushing docker image using Dockerfile..., then Hugo’s summary of the pages it built, and its Dockerfile tab shows your file. When the status changes to Deployed, click the domain under the application’s name: the Ananke home page opens with the post Welcome to Storefront. From a terminal, curl -s https://storefront-x7k2m.sevalla.app/ | grep -o '<link rel=canonical[^>]*>' prints <link rel=canonical href=https://storefront-x7k2m.sevalla.app/>. If you deploy before you switch the build strategy, Ingot fails or starts a server that doesn’t exist: switch to Dockerfile and deploy again. See Dockerfile.
The Update build strategy sheet with Dockerfile selected, the Dockerfile path, the Docker context and the Docker registry credentialThe Update build strategy sheet with Dockerfile selected, the Dockerfile path, the Docker context and the Docker registry credential

Deploy changes

With Auto-deploy on, every push to the branch starts a deployment of the static site or the application; otherwise, deploy each change from the Deployments tab. To move to a newer Hugo release, update hugo-bin with npm install hugo-bin@latest --save-dev for the static site, and the image tag in the Dockerfile for the application. See Static site deployments and Deployments.

Add a custom domain

To serve the site from your own domain, such as blog.example.com, add the domain on the static site’s Domains tab and create the DNS records that Sevalla shows, then set HUGO_BASEURL to https://blog.example.com/ and deploy again. Applications have a Domains tab too, but custom domains need a web process larger than H1. See Custom domains.

Deploy with the API

This sequence creates the static site from Deploy as a static site and the application from Deploy with a Dockerfile through the Sevalla API, so an agent or a script can run it as is. Run the first five steps for the static site, the rest for the application. Each step names the operation, shows the curl command, and gives the matching CLI command and MCP server tool. You need:
  • An API key with the Company developer role, in the SEVALLA_API_KEY environment variable. The CLI reads the same key from SEVALLA_API_TOKEN.
  • curl and jq, and the project pushed to GitHub as in Prepare the project for Sevalla.
  • A GitHub account that can read the repository, connected to Sevalla. See Git providers. The API can’t list connected accounts: when none can read the repository, Create static site and Create application fail with 400 and Cannot access repository https://github.com/acme/storefront: Repository not found or you don't have access.
A call that fails returns status and message instead of the resource, and jq -r .id prints null: stop and read the message.
1

Create the static site

Create static site (createStaticSiteV3) takes the build settings: node_version lts is the dashboard’s Latest LTS, and published_directory is the Publish directory.
CLI: sevalla static-sites create --name storefront-blog --repository https://github.com/acme/storefront --branch main --source privateGit --git-type github --build-command "npm run build" --publish-directory public --node-version lts. MCP: create_static_site with the same fields.
2

Set the site's address

Get static site (getStaticSiteV3) returns the site’s domain in hostname, a few seconds after the site is created; until then it’s null. Create static site environment variable (createStaticSiteEnvVarV3) adds HUGO_BASEURL, with a trailing slash.
CLI: sevalla static-sites get "$SITE_ID" and sevalla static-sites env-vars create "$SITE_ID" --key HUGO_BASEURL --value "$SITE_URL/". MCP: get_static_site, then set_environment_variables with scope static_site.
3

Deploy the static site

Deploy static site (deployStaticSiteV3) starts the first deployment. Unlike the dashboard, creating a static site through the API doesn’t start one.
CLI: sevalla static-sites deployments trigger "$SITE_ID". MCP: trigger_deployment with service_type static_site.
4

Wait for the static site deployment

Get static site deployment (getStaticSiteDeploymentV3) returns the deployment’s status: waiting, then inProgress, then success or failed. The loop polls every 15 seconds for up to 30 minutes and keeps polling through a failed request.
If it’s failed, Get static site deployment logs (getStaticSiteDeploymentLogsV3) returns the build log. CLI: sevalla static-sites deployments get "$SITE_DEPLOYMENT_ID" --site-id "$SITE_ID" and sevalla static-sites deployments logs "$SITE_DEPLOYMENT_ID" --site-id "$SITE_ID". MCP: get_deployment and get_deployment_logs with service_type static_site.
5

Check the static site

The home page has the site’s title, and its canonical link uses HUGO_BASEURL. These commands print <title>Storefront</title>, then <link rel=canonical href=https://storefront-blog-x7k2m.sevalla.page/> with your site’s domain:
6

Look up the location and the size

List clusters (listClustersV3) returns every location your company can use, with its region ID. This guide uses Frankfurt, Germany, eu-frankfurt-1. List process resource types (listProcessResourceTypesV3) returns the process sizes.
CLI: sevalla resources clusters and sevalla resources process-types. MCP: get_platform_options.
7

Create the application

Create application (createApplicationV3) creates the application with a web process and doesn’t deploy it.
CLI: sevalla apps create --name storefront --cluster "$CLUSTER_ID" --source privateGit --git-type github --repository https://github.com/acme/storefront --branch main. MCP: create_application with deploy set to false.
8

Size the web process

The API creates the web process at S1, while the dashboard creates it at H1, which is enough for Caddy. List processes (listProcessesV3) returns the web process, and Update process (updateProcessV3) sets its size. Keep S1, and skip this step, to add a custom domain later.
CLI: sevalla apps processes update "$WEB_ID" --app-id "$APP_ID" --resource-type "$H1_ID". MCP: scale_process with size_id and apply_now set to false.
9

Choose the Dockerfile build strategy

Update application (updateApplicationV3) sets the build strategy. Ingot has no Hugo builder, so deploy only after this step.
The same call turns on auto-deploy. CLI: sevalla apps update "$APP_ID" --data with the same body. MCP: sevalla_api_update with updateApplicationV3 and the same body.
10

Set the site's address

List application domains (listApplicationDomainsV3) returns the application’s domain, and Create environment variable (createEnvironmentVariableV3) adds HUGO_BASEURL, available during the build only: the Dockerfile reads it as a build argument.
CLI: sevalla apps domains list --app-id "$APP_ID" and sevalla apps env-vars create "$APP_ID" --key HUGO_BASEURL --value "$APP_URL/", which makes it available during runtime too. MCP: get_application, then set_environment_variables with runtime false and buildtime true.
11

Deploy

Trigger deployment (triggerDeploymentV3) builds the latest commit of the branch and rolls it out.
CLI: sevalla apps deployments trigger "$APP_ID". MCP: trigger_deployment.
12

Wait for the deployment

Get deployment (getDeploymentV3) returns the deployment’s status: waiting, then inProgress, then success or failed. The loop polls every 15 seconds for up to 30 minutes and keeps polling through a failed request.
CLI: sevalla apps deployments get "$DEPLOYMENT_ID" --app-id "$APP_ID". MCP: get_deployment.
13

Check the application

Get application deployment logs (getApplicationDeploymentLogsV3) returns the build log, which shows Hugo’s summary and, on a failed deployment, ends with the error:
After the build log, the output is <title>Storefront</title>, then <link rel=canonical href=https://storefront-x7k2m.sevalla.app/> with your application’s domain.CLI: sevalla apps deployments logs "$DEPLOYMENT_ID" --app-id "$APP_ID". MCP: get_deployment_logs.