Skip to main content

Hosting a Static Website

You can host a static website on Hostim.dev. Build the site on your machine, then deploy the build folder with the CLI. The CLI adds an nginx server for you, so you do not need a Dockerfile.

npm run build
hostim deploy site --source dist --plan sa-1-1

The deploy takes about 30 seconds and prints the URL of the site.

How it works​

When the folder you pass with --source has an index.html at the root and no Dockerfile, the CLI deploys it as a static site:

  1. The CLI uploads every file in the folder. A build folder in .gitignore is uploaded too.
  2. The CLI adds a Dockerfile and an nginx config. The site is served by nginx:stable-alpine on port 80, so you do not pass --port.
  3. Hostim builds the image and starts the app. The CLI prints the URL.

The nginx config does these things:

  • /about serves about.html, about/index.html or about/. Clean URLs work for every generator below.
  • A missing page returns the site's own 404.html.
  • Text files (HTML, CSS, JavaScript, JSON, SVG, RSS) are sent with gzip.
  • Files in /_astro/, /_next/static/ and /assets/ are cached by browsers for one year. These folders hold files with a hash in the name, so a new deploy uses new names.
  • Redirects such as /blog to /blog/ stay on https://.

The added Dockerfile and nginx config are not served as files.

To force static mode, pass --static. This is needed when the build folder has a Dockerfile in it, for example with Jekyll (see below). With --static, the folder's own Dockerfile and .dockerignore are ignored.

Build folder per generator​

GeneratorBuild commandFolder to deploy
Astronpm run builddist
Next.js (static export)npm run build with output: "export"out
Docusaurusnpm run buildbuild
Hugohugo --minifypublic
Jekyllbundle exec jekyll build_site (with --static)
Plain HTMLnonethe folder with index.html

All six were deployed and tested on Hostim.dev in October 2026.

Set the site URL first​

Most generators write the site URL into the sitemap, the RSS feed and the canonical links. The starter templates ship with a placeholder such as https://example.com. Set the real URL before you build:

GeneratorSettingFile
Astrositeastro.config.mjs
Docusaurusurldocusaurus.config.ts
HugobaseURLhugo.toml
Jekyllurl_config.yml

Use your own domain if you add one. Otherwise use the URL the first deploy prints, then build and deploy again.

Astro​

npm run build
hostim deploy site --source dist --plan sa-1-1

This mode is for the default static output. An Astro site with server-side rendering (the @astrojs/node adapter) is a Node app; see Astro hosting.

The Astro starter templates have no 404.html. Add src/pages/404.astro, or nginx shows its own 404 page.

Next.js static export​

Set output: "export" in next.config.ts:

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
output: "export",
};

export default nextConfig;

Then build and deploy the out folder:

npm run build
hostim deploy site --source out --plan sa-1-1

Static export does not support some features. These include API routes, middleware, image optimization with the default loader, and features that need a server at request time. Next.js 16 also fails the build with cacheComponents turned on. Remove it from the config for a static export.

A Next.js app that needs a server runs as a Node container instead. See Next.js hosting.

Docusaurus​

npm run build
hostim deploy site --source build --plan sa-1-1

Hugo​

hugo --minify
hostim deploy site --source public --plan sa-1-1

Themes often need a recent Hugo version. For example, PaperMod needs Hugo 0.158 or newer. If the build fails with a version error, update Hugo.

Jekyll​

Jekyll copies every file without a leading _ into _site. If the project has a Dockerfile, it ends up in _site too, and the CLI no longer detects a static site. Pass --static:

JEKYLL_ENV=production bundle exec jekyll build
hostim deploy site --source _site --static --plan sa-1-1

Also add the files you do not want to publish to exclude in _config.yml. Otherwise they are public on the site:

exclude:
- Dockerfile
- nginx.conf

Plain HTML​

Deploy the folder that holds index.html:

hostim deploy site --source . --plan sa-1-1

Outside a Git repository, every file in the folder is uploaded. Keep private files out of the folder.

Redeploy​

Build again and run the same command:

npm run build
hostim deploy site --source dist

The CLI uploads only if the files changed. An uploaded folder is kept for 7 days. After that, deploy the folder again to rebuild. See Deploying from a Local Directory.

Add your own domain​

Add the domain when you create the app:

hostim deploy site --source dist --plan sa-1-1 --domain www.example.com

Point the domain at Hostim and HTTPS is set up automatically. See Domains.

Build in Docker or from Git​

The steps above build the site on your machine. To build on Hostim instead, for example from a Git repository, add a multi-stage Dockerfile. The first stage builds the site. The second stage serves it with nginx.

Astro, Next.js and Docusaurus (change dist to out or build):

FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

Hugo:

FROM ghcr.io/gohugoio/hugo:latest AS build
USER root
WORKDIR /src
COPY . .
RUN hugo --minify

FROM nginx:alpine
COPY --from=build /src/public /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

Jekyll:

FROM ruby:3.3 AS build
WORKDIR /src
COPY Gemfile* ./
RUN bundle install
COPY . .
RUN JEKYLL_ENV=production bundle exec jekyll build

FROM nginx:alpine
COPY --from=build /src/_site /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

Put this nginx.conf next to the Dockerfile. It is the same config the CLI adds in static mode:

server {
listen 80;
root /usr/share/nginx/html;
server_tokens off;

# Behind the ingress nginx sees plain HTTP: keep redirects relative so
# /about -> /about/ stays on https.
absolute_redirect off;

gzip on;
gzip_min_length 1024;
gzip_types text/css text/plain text/xml application/javascript application/json
application/xml application/rss+xml application/atom+xml image/svg+xml;

location / {
# $uri.html serves Next.js static export routes (/about -> about.html).
try_files $uri $uri.html $uri/ =404;
}

# Content-hashed asset folders of Astro, Next.js and Docusaurus.
location ~ ^/(_astro|_next/static|assets)/ {
add_header Cache-Control "public, max-age=31536000, immutable";
}

error_page 404 /404.html;
}

Do not use the default config of the nginx image. It sends no gzip, it sends redirects to http://, and it returns 403 for Next.js routes such as /about.

Deploy the project folder, or deploy from Git:

hostim deploy site --source . --plan sa-1-1 --port 80
hostim deploy site --git https://github.com/me/site --branch main --plan sa-1-1 --port 80

To rebuild on every push, add the Hostim GitHub Action to the repository. See Deploying from a Git Repository and GitHub Actions Integration.

Limits​

  • No CDN. The site runs as one nginx container on servers in Germany. Visitors far from Europe wait longer for the first byte. To serve a global audience faster, put a CDN such as Cloudflare in front of your domain.
  • DIY previews. There is no preview button. A GitHub workflow can deploy each pull request as its own app and delete it when the PR closes. A two-day preview costs a few cents. See Preview environments per pull request.
  • No forms or functions. Hostim does not process form posts or run serverless functions. Run a small API as a second app in the same project instead.

Pricing​

A static site is an app. The smallest plan, sa-1-1 (1 vCPU, 1 GB RAM), costs €2.50 per month. Traffic is included. There are no bandwidth, build-minute or per-request charges. See Pricing.

Troubleshooting​

The CLI asks for --port. The folder is not detected as a static site. It needs an index.html at the root and no Dockerfile. Check that you passed the build folder, not the project folder. If the build folder has a Dockerfile, pass --static.

The CLI says the directory has no Dockerfile. The folder has no index.html at the root. Build the site first, then pass the build folder with --source.

Links point at example.com. The site URL in the generator config is still the placeholder. Set it, build and deploy again.

A page returns nginx's own 404 page. The build has no 404.html at the root. Add a 404 page to the site.