gohugoio/hugo · error

server startup failed

Error message

server startup failed: %s

What it means

`createServerPorts` calls `net.Listen("tcp", host:port)` for the configured port. If that fails AND the user set `--port` explicitly, Hugo refuses to fall back and wraps the listen error as `server startup failed`. The explicit-port contract is: the user meant this port, so do not silently relocate.

Solutions

  1. Free the port: find and stop the occupying process (`lsof -i :PORT`).
  2. Choose a different `--port` that is known free.
  3. Use a non-privileged port (>1024) unless running as root with explicit need.
  4. Drop the explicit `--port` to let Hugo auto-select an alternative.

Example fix

// before
// $ hugo server --port=1313   (already in use)
// after
// $ lsof -ti:1313 | xargs kill ; hugo server --port=1313
Defensive patterns

Strategy: validation

Validate before calling

import (
  "net"
  "strconv"
)
func validatePortFree(host string, port int) error {
  ln, err := net.Listen("tcp", net.JoinHostPort(host, strconv.Itoa(port)))
  if err != nil { return err }
  ln.Close()
  return nil
}

Type guard

null

Try / catch

// If the pinned port is busy, free it or let Hugo auto-select by
// dropping the explicit --port.
if err := cmd.Execute(); err != nil {
  if strings.Contains(err.Error(), "server startup failed") { /* free port or drop --port */ }
}

Prevention

When it happens

Trigger: Fires on the first port (`i == 0`) when `--port` was changed on the CLI and `net.Listen` returns an error — typically `bind: address already in use` or `bind: permission denied` (privileged port).

Common situations: Another process (often a previous `hugo server` or a dev server) holds the chosen port; requesting a privileged port `<1024` as a non-root user; a port blocked by firewall/SELinux.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/19d4c96d7a846ec1. Report an issue: GitHub.

Appendix: source

Thrown at commands/server.go:774

	c.withConf(func(conf *commonConfig) {
		isMultihost := conf.configs.IsMultihost
		c.serverPorts = make([]serverPortListener, 1)
		if isMultihost {
			if !c.serverAppend {
				cerr = errors.New("--appendPort=false not supported when in multihost mode")
				return
			}
			c.serverPorts = make([]serverPortListener, len(conf.configs.Languages))
		}
		currentServerPort := c.serverPort
		for i := range c.serverPorts {
			l, err := net.Listen("tcp", net.JoinHostPort(c.serverInterface, strconv.Itoa(currentServerPort)))
			if err == nil {
				c.serverPorts[i] = serverPortListener{ln: l, p: currentServerPort}
			} else {
				if i == 0 && flags.Changed("port") {
					// port set explicitly by user -- he/she probably meant it!
					cerr = fmt.Errorf("server startup failed: %s", err)
					return
				}
				c.r.Println("port", currentServerPort, "already in use, attempting to use an available port")
				l, sp, err := helpers.TCPListen()
				if err != nil {
					cerr = fmt.Errorf("unable to find alternative port to use: %s", err)
					return
				}
				c.serverPorts[i] = serverPortListener{ln: l, p: sp.Port}
			}

			currentServerPort = c.serverPorts[i].p + 1
		}
	})

	return cerr
}

View on GitHub (pinned to 52c9bd7908)