cloudflare/cloudflared · error

use `cloudflared tunnel run` to start tunnel %s

Error message

use `cloudflared tunnel run` to start tunnel %s

What it means

cloudflared refuses to start a tunnel when the configuration file already contains a tunnel ID, because providing a TunnelID means `tunnel run` semantics were intended, not `tunnel` (proxy-dns style) invocation. The library throws this to catch users who configured a tunnel but invoked the wrong subcommand. It is a guard against running named tunnels without the proper `run` command flow.

Source

Thrown at cmd/cloudflared/tunnel/cmd.go:262

		}
		tunnelURL := c.String("url")
		if tunnelURL == hostname && tunnelURL != "" && hostname != "" {
			return fmt.Errorf("hostname and url shouldn't match. See --help for more information")
		}

		return runAdhocNamedTunnel(sc, name, c.String(CredFileFlag))
	}

	// Run a quick tunnel
	// A unauthenticated named tunnel hosted on <random>.<quick-tunnels-service>.com
	shouldRunQuickTunnel := c.IsSet("url") || c.IsSet(ingress.HelloWorldFlag)
	if c.String("quick-service") != "" && shouldRunQuickTunnel {
		return RunQuickTunnel(sc)
	}

	// If user provides a config, check to see if they meant to use `tunnel run` instead
	if ref := config.GetConfiguration().TunnelID; ref != "" {
		return fmt.Errorf("use `cloudflared tunnel run` to start tunnel %s", ref)
	}

	// Classic tunnel usage is no longer supported
	if c.String("hostname") != "" {
		return errDeprecatedClassicTunnel
	}

	return errors.New(tunnelCmdErrorMessage)
}

func Init(info *cliutil.BuildInfo, gracefulShutdown chan struct{}) {
	buildInfo, graceShutdownC = info, gracefulShutdown
}

// runAdhocNamedTunnel create, route and run a named tunnel in one command
func runAdhocNamedTunnel(sc *subcommandContext, name, credentialsOutputPath string) error {
	tunnel, ok, err := sc.tunnelActive(name)
	if err != nil || !ok {

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Change the command to `cloudflared tunnel run` (optionally with `--token` or credentials flags).
  2. If you really intend non-run behavior, remove the `tunnel:` (TunnelID) entry from the config file or unset it via `TUNNEL_ID` env override.
  3. If embedding cloudflared, invoke the `tunnel run` action instead of the bare TunnelCommand when configuration contains a tunnel ID.

Example fix

// before
ExecStart=/usr/local/bin/cloudflared tunnel --config /etc/cloudflared/config.yml
// after
ExecStart=/usr/local/bin/cloudflared tunnel --config /etc/cloudflared/config.yml run
Defensive patterns

Strategy: validation

Validate before calling

// shell: fail fast if config declares a tunnel ID
if grep -qE '^tunnel:' /etc/cloudflared/config.yml; then
  exec cloudflared tunnel --config /etc/cloudflared/config.yml run
fi
exec cloudflared tunnel --config /etc/cloudflared/config.yml

Prevention

When it happens

Trigger: Running `cloudflared tunnel` (or an embedded TunnelCommand via the CLI app) when the resolved config file has `tunnel: <ID>` set, instead of running `cloudflared tunnel run`. Also triggered by script wrappers that call TunnelCommand directly with a config containing TunnelID.

Common situations: Users follow an old tutorial that says `cloudflared tunnel <UUID>` as a positional argument style; or a systemd unit / Docker entrypoint invokes `cloudflared tunnel` with a config file that lists tunnel ID. Also common after moving credentials into config but forgetting to change the command to `run`.

Understand the failure class

Background: "mutually exclusive" flag errors: what "can't supply both nx and xx", "--raw is not compatible with -i" and "cannot be used with" mean, and how to fix them — this error's family across 29 libraries.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/d29933d19dcf2875. Report an issue: GitHub.