tnl.dev :: docs
project configuration.
Configure named local services and public URL settings.
create a project file
Project configuration gives tnl dev a command to run and tnl publish a target to reach. Keep it with your application so a worktree can run the same services with its own default hostnames.
Run tnl init in the project directory to create tnl.config.ts, or write tnl.yml, tnl.yaml, or tnl.json yourself. Use one file per directory. A TypeScript file needs Node.js 22.18 or newer and @tnldotdev/tnl/config for defineConfig. See the config reference for discovery rules and every field.
configure services
Name each local service you want to start or publish. This project starts web on port 3000 and publishes an already-running api on port 4000. Use whichever file format fits your project:
import { defineConfig } from "@tnldotdev/tnl/config";
export default defineConfig({
services: {
web: {
directory: "apps/web",
dev: { command: ["node", "server.js"], port: 3000 },
},
api: {
directory: "apps/api",
publish: { target: 4000 },
},
},
});tnl dev web
tnl publish apidev starts the child command in the service directory and publishes it when the target is ready. publish connects to an already-running local service. Adjust the commands, paths, and ports to match your app. With one configured service you can omit its name; with several, specify one. For framework target detection and parallel worktrees, see Development.
choose a server, team, and domain
Set server to a control URL if this project uses another tnl server. Otherwise the selected server or https://control.tnl.dev applies. Set team to a unique lowercase team name or team ID to keep this project on that team; a service can override either setting for tnl dev and tnl publish. Management commands use the project's root server and team. An explicit --server pointing to another server ignores the project team; pass --team to select a team for that invocation. Without a project team, tnl team use selects one for future commands on the server, falling back to your personal team. See Teams.
Set tunnel.domain to a ready domain available to the team when this project should use a different domain from the team default. It changes where this project's worktree URLs go; it does not change the team's default. Use tnl domain list to see available domains. See Domains.
set defaults for public urls
By default, tnl names each service's public URL from the project and worktree. Only your current IP can visit. See who can visit? to change that.
Set tunnel.open: true to open the public URL in a browser when a tunnel is ready. To use a fixed name beneath your member namespace, set services.NAME.tunnel.name for that service. Fixed names are reused across worktrees, so leave name unset for separate worktree URLs. An exact team-shared address can use services.NAME.tunnel.publicURL; see Domains. With named services, fixed name and publicURL values belong under one service, not at the root. See the config reference for all tunnel settings.
check and generate project metadata
tnl config path
tnl config check
tnl config generatepath reports the selected file without evaluating it. check validates it. generate writes browser-safe project metadata and TypeScript declarations under .tnl/ for framework integrations; it needs access to your selected server. See Development for using that metadata in an app.