26 min read

How to Install iii for a Self-Hosted Backend

Install and verify iii, scaffold a backend project, start it locally, and expose its confirmed HTTP endpoint securely with Localtonet.

Developer Tools ยท iii Installation ยท Localtonet ยท 2026

Create a current iii project, configure its provider safely, verify the local console, and publish only the application route you need

iii organizes backend capabilities around Workers, Functions, and Triggers. Its current installation workflow is interactive: install the CLI and engine, initialize a project, choose an LLM provider or the no-LLM template, prepare any required credentials, and start the project. The local iii console is documented on port 3113, but that console is not the same thing as a user-defined HTTP Trigger. This guide explains that distinction, covers foreground and Docker operation, and shows how to expose a verified application endpoint through Localtonet without publishing unnecessary runtime or administrative surfaces.

๐Ÿ”’ Keep provider credentials and operational interfaces private ๐ŸŒ Separate the iii console from application HTTP routes โšก Install, configure, start, verify, and troubleshoot iii
A locally hosted iii project connected to a remote client through a Localtonet tunnel.
Start and verify iii locally first. If the project defines an application HTTP route, Localtonet can then forward that specific service without requiring inbound router port forwarding.

Understand what a self-hosted iii project runs

iii provides a common runtime model for capabilities that might otherwise be implemented through separate backend systems. Its three core primitives are Worker, Function, and Trigger. A Worker is a participating process. A Function is a named unit of work hosted by a Worker. A Trigger declares what causes a Function to run.

Triggers can represent direct function calls, HTTP requests, cron schedules, queue subscriptions, state changes, stream events, and other supported event sources. Workers register their Functions and Triggers with the iii engine, allowing participating processes to discover and call capabilities through a shared system surface.

โš™๏ธ Workers host capabilities A TypeScript API, Python pipeline, Rust service, Go service, or another compatible process can participate as a Worker and register what it provides.
๐Ÿงฉ Functions define work Functions use stable identifiers, receive input, perform work, and can return output to their caller.
โšก Triggers initiate execution An HTTP request is only one kind of Trigger. A project can be useful without defining any application HTTP route.
๐Ÿ” The console shows runtime state The iii console is an operations and development interface for inspecting workers, functions, triggers, queues, traces, logs, and live state.

The current project flow declares project workers in worker-compose.yaml and starts the engine and those workers with iii compose --up. The generated project depends on the choice made during interactive initialization. Selecting an LLM provider prepares a provider-oriented project, while selecting no LLM creates the base template.

The console is not your application API

Current iii installation documentation says that a started project can be explored through the console on 127.0.0.1:3113. This does not mean every project has an application HTTP Trigger on port 3113. The no-LLM base template starts only the console. To expose business logic over HTTP, the running system must also contain a Worker that declares the required HTTP Trigger.

The current iii repository is the best place to inspect the project structure, SDK packages, console source, engine source, and current README. See the iii repository alongside the current iii documentation.

Licensing and version compatibility require component-specific review

The current repository license table identifies engine/ as Elastic License 2.0 and identifies sdk/, console/, docs/, and website/ as Apache License 2.0. The available table does not separately list every installed CLI artifact, so this guide does not assign a license to the CLI by inference. Review the repository's current license files and the directories for the exact components you intend to use.

Likewise, do not assume a universal same-minor compatibility rule for every engine and SDK release unless the release documentation for your versions states it. Record the installed engine version, record each SDK package version, and consult the applicable release notes before upgrading. As an example of version-specific release information, see the iii 0.22.1 release notes. A specific release page is a historical snapshot, not proof that it is the latest available version.

Prepare the host and choose a provider path

The documented installer is downloaded and executed through a shell with curl. You need a compatible command-line environment, network access to the installation host, permission to install the executable, and a writable directory for the project. The supplied current documentation does not provide a complete operating-system support matrix or universal hardware minimum, so verify platform support for the machine on which you plan to run iii.

Prepare the following before installation:

  • A shell capable of running the documented installer.
  • curl and network access to install.iii.dev.
  • Permission to place and run the installed executable.
  • A writable parent directory for the new project.
  • A browser for opening the local console.
  • A second terminal for local tests and provider commands while iii remains active.
  • Docker with the Compose plugin only if you plan to use the optional generated Docker deployment.
  • The required provider credential or provider login, unless you select no LLM.
Review remote installation scripts according to your security policy

Piping a downloaded script directly into a shell executes it immediately. The command in this guide is the installation method documented by iii, but your organization may require downloading, inspecting, approving, or internally mirroring the script before execution. Follow that policy rather than bypassing it.

Choose the provider before you initialize the project

The current installer walkthrough asks you to select an LLM provider or select no LLM during project setup. Most provider choices require an API key in the generated .env file. OpenAI Codex and GitHub Copilot use login-based preparation instead, and llama.cpp points to a locally operated llama server.

Setup choice Required preparation Documented behavior or follow-up
No LLM No provider credential The base template starts only the iii console. It does not create an application HTTP Trigger by itself.
OpenAI API Set OPENAI_API_KEY in .env The selected OpenAI provider is enabled by default in that provider template.
Anthropic Set ANTHROPIC_API_KEY in .env The selected Anthropic provider is enabled by default in that provider template.
OpenAI Codex Run codex login so ~/.codex/auth.json exists Add provider-openai-codex to the running project.
DeepSeek Set DEEPSEEK_API_KEY in .env Add provider-deepseek to the running project.
Kimi (Moonshot) Set MOONSHOT_API_KEY in .env Add provider-kimi to the running project.
OpenRouter Set OPENROUTER_API_KEY in .env Add provider-openrouter to the running project.
xAI (Grok) Set XAI_API_KEY in .env Add provider-xai to the running project.
Z.AI Set ZAI_API_KEY in .env Add provider-zai to the running project.
GitHub Copilot A GitHub Copilot subscription rather than an API key Add the provider, then start its documented login flow.
llama.cpp Run your own llama server and review .env The documented default target is http://127.0.0.1:8080; add provider-llamacpp to the project.

Never paste a real provider key into a command shown in documentation, a support ticket, a screenshot, or source control. Put the required value in the generated .env file, restrict access to that file, and ensure it is excluded from commits. If a credential has been exposed, revoke or rotate it through the provider rather than merely deleting it from the latest commit.

Install iii and verify the command

Follow the current iii installation guide for the authoritative setup flow. Its installation command downloads and runs the iii installer:

curl -fsSL https://install.iii.dev/iii/main/install.sh | sh

The current install page proceeds from installation into interactive project initialization. Running iii --version is still a useful independent check, but it is not presented as the second step of the current install recipe. The version check appears in older versioned documentation and remains practical for confirming that your shell can locate the command.

iii --version

A successful result should print a version. Record that value when opening an issue or comparing behavior with release documentation.

If the shell cannot find iii

Read the installer's output for path instructions. If it changed shell configuration, open a fresh terminal and retry the version command. Confirm that installation and verification use the intended user account, and inspect that user's command search path.

The current evidence does not establish one universal installation directory. Do not guess a location, move an unfamiliar binary, or add a broad writable directory to PATH. In centrally managed environments, use the location and permissions approved by the administrator.

Initialize, configure, and start the project

A three-stage workflow for initializing, configuring, and starting an iii project.
Current setup includes an interactive provider choice and any required credential preparation before the project is started.
1

Initialize a project

Run iii project init my-app from the parent directory where the project should be created. The setup asks you to choose no LLM or one of the documented providers.

2

Enter the generated directory

Change into my-app. Subsequent project and Compose commands should be run from this project root.

3

Prepare the selected provider

For an API-key provider, edit .env and set only the variable required for that provider. For Codex, complete codex login. No provider credential is needed for the no-LLM path.

4

Start the engine and project

Run iii compose --up from the project root. Keep the terminal open so you can see startup and provider errors.

5

Add a provider when the selected path requires it

In a second terminal, run the documented compose::add command for Codex, DeepSeek, Kimi, OpenRouter, xAI, Z.AI, GitHub Copilot, or llama.cpp. GitHub Copilot also requires its provider login trigger after the worker starts.

iii project init my-app
cd my-app
iii compose --up

The initializer is interactive, so choose the provider that matches the credential or local service you prepared. Do not enable several providers merely because they appear in the documentation. Start with the one your application requires and keep unrelated credentials out of the project.

Provider commands used after startup

The current install page documents the following commands for provider choices that are added to a running project:

iii trigger compose::add worker=provider-openai-codex
iii trigger compose::add worker=provider-deepseek
iii trigger compose::add worker=provider-kimi
iii trigger compose::add worker=provider-openrouter
iii trigger compose::add worker=provider-xai
iii trigger compose::add worker=provider-zai
iii trigger compose::add worker=provider-github-copilot
iii trigger compose::add worker=provider-llamacpp

Run only the command for your selected provider. For GitHub Copilot, start the documented sign-in flow after its provider worker is running:

iii trigger provider::github-copilot::login::start
Do not expose or commit provider credentials

Keep .env, ~/.codex/auth.json, and any provider login state out of repositories, build output, public screenshots, and shared logs. A Localtonet tunnel should never target a file browser, shell, or other service that could reveal these files.

What the no-LLM template gives you

The no-LLM choice creates a valid base project, but the current install documentation explicitly says that it starts only the console. This is useful for confirming that the engine and console can run, but it does not create a business API, webhook receiver, or other application endpoint.

To serve an application request, add or develop a Worker that registers a Function and binds it to an HTTP Trigger. That Trigger determines the method and route used by the application. Do not point remote traffic at the console and treat it as a substitute for an application endpoint.

Avoid mixing current and old quickstarts

Older versioned documentation uses commands such as iii create --template quickstart --directory quickstart, starts the engine with iii --config iii-config.yaml, and starts the console separately with iii console. Those instructions belong to an older documentation generation.

Use the current interactive iii project init and iii compose --up flow for this tutorial. If you are maintaining an older project, match its commands to the installed version and its versioned documentation rather than combining files and commands from different generations.

Verify the console separately from application HTTP Triggers

A local iii service being tested at its confirmed address before remote access is configured.
Verify each local surface according to its purpose: port 3113 for the console, and a separately confirmed route for an application HTTP Trigger.

After iii compose --up remains active, first inspect its terminal output for configuration failures. A missing API key, unsuccessful provider login, unreachable llama.cpp server, or failed Worker can leave the engine partially operational while the intended project capability is unavailable.

Open the local console

The current install page names 127.0.0.1:3113 as the local console address. However, that same page is internally inconsistent about the URL scheme: its introduction shows https://127.0.0.1:3113, while its final instruction says to open http://127.0.0.1:3113.

Confirm the console scheme from the running project

Do not infer TLS solely from the introductory HTTPS reference. Start with the exact address printed by the installed version's startup output. The current page's final browser instruction uses http://127.0.0.1:3113, while another sentence uses HTTPS. If one scheme fails, inspect the browser error and runtime output before changing network or tunnel settings.

A practical local browser check based on the page's final instruction is:

http://127.0.0.1:3113

Successful console access confirms that the console listener is reachable. It does not confirm that a provider works, that a Worker has registered an application Function, or that an HTTP Trigger exists.

Verify an application HTTP Trigger

If your project includes an application Worker, identify the route it registers. Record the local origin, path, HTTP method, required headers, authentication, and request body. Then reproduce the request from the iii host before creating any public tunnel.

curl -i http://127.0.0.1:PORT/PATH

PORT and PATH are placeholders, not iii defaults. Replace them with the values defined by your Worker and HTTP Trigger. If the Trigger expects POST, JSON, or authentication, include those requirements in the test. A browser address bar performs a simple GET and is not sufficient for every route.

Surface What success proves What it does not prove
CLI version The shell can find and execute iii That a project or provider is configured
Engine startup The core process remains active That every project Worker started
Console on port 3113 The local console is reachable That an application HTTP Trigger exists
Provider Worker The selected provider loaded without an immediate configuration error That every model request or application workflow succeeds
Application Trigger The requested method and route reach the intended Function That the route is safe to publish publicly
Localtonet public URL The running tunnel can reach the selected local service That the application has adequate authorization

Use the optional Docker deployment workflow

iii also documents a Docker-oriented deployment flow. See the current iii deployment documentation before adapting generated assets for production.

For a new Docker-oriented project, use:

iii project init --docker

To add Docker assets to an existing project, run this from the project root:

iii project generate-docker

Both forms emit Dockerfile, docker-compose.yml, and .env. Re-running the generator does not overwrite existing files, so local edits remain in place. This also means regeneration will not automatically merge future template changes into files you have already generated.

Start, inspect, and stop the Docker stack

docker compose up -d
docker compose ps
docker compose logs
docker compose down

docker compose up -d starts the stack in detached mode. Use docker compose ps to inspect service state and docker compose logs to review startup or provider failures. Use docker compose down for a normal shutdown when the deployment should stop. Review your volume and persistence configuration before adding flags that remove stored data.

The generated deployment documents three engine transport ports:

Port Documented service Exposure guidance
49134 SDK WebSocket for Worker connections Do not publish it merely because it is listening. It is not an application REST endpoint.
3111 REST API Confirm the required application route and its authorization before exposing it.
3112 Stream API Streaming and upgraded connections can require different reverse-proxy handling from ordinary HTTP requests.

These Docker transport ports do not redefine the console. Port 3113 remains the documented local console address in the current install flow. Likewise, a listening REST port does not guarantee that a user-defined HTTP Trigger exists at a particular path.

The generated Dockerfile builds against iiidev/iii:latest and is documented as distroless and non-root. The generated Compose file includes commented Redis and RabbitMQ services for Workers that need external adapters. Enable those services only when a declared Worker requires them, and configure credentials, persistence, backups, and network access according to your deployment policy.

TLS and reverse-proxy boundaries

The iii deployment documentation says that the engine does not terminate TLS. Its reverse-proxy examples route /api/* to port 3111, /stream/* to port 3112, and /ws to port 49134. Those examples require additional production work for certificates, headers, authentication, access logging, request limits, and application-specific routing.

A Localtonet HTTP tunnel provides a public HTTPS address for the selected local HTTP target, with TLS terminated at the tunnel edge. It does not automatically combine iii's REST, stream, WebSocket, and console ports into one application. If your application needs several transport surfaces under one origin, configure and verify a suitable local reverse proxy first, then point the tunnel at that proxy.

Expose a verified iii application route with Localtonet

Remote HTTP traffic passing through a Localtonet tunnel to a confirmed local iii application endpoint.
The tunnel should target a locally verified application service, not the iii console or every engine transport port.

Once an application HTTP Trigger works locally, Localtonet can make that HTTP service reachable remotely. Our client establishes an outbound connection from the selected device to a Localtonet relay server, so you do not need inbound router port forwarding, firewall changes, VPN setup, or a public IP address.

1

Install and run the Localtonet client

Run our client on the iii host or another device that can reach the verified application service. Keep the iii project active during setup.

2

Select the correct device

Authenticate or select the device-specific token for the client that can reach iii. Never copy that token into source code, screenshots, logs, or documentation.

3

Select an available relay server

Choose a currently available relay server or region from our dashboard. Obtain current values from the product rather than copying a hardcoded server code.

4

Create the HTTP tunnel

Set the local target to the exact IP address and port that passed the application test. The HTTP route remains part of the request URL and is not entered as a replacement for the local port.

5

Start the tunnel

Creating a tunnel does not start it. Use the Start button and wait until the selected client and tunnel are connected.

6

Test the public application route

Append the verified Trigger path to the assigned public HTTPS address and repeat the same method, headers, authentication, and body used in the local test. Stop or delete the tunnel when remote access is no longer required.

See our Localtonet HTTP tunnel documentation for the current dashboard workflow. Available relay servers, dashboard options, and plan-specific capabilities can vary, so check the current product during setup.

Do not tunnel the console as an application endpoint

The console can expose logs, traces, Workers, Functions, Triggers, queues, and runtime state. Publish only the application route needed by the remote client. A public HTTPS address provides transport reachability, not application authorization, so require appropriate authentication and apply least privilege.

The public endpoint depends on both lifecycles

The iii project and required Workers must remain active, and the Localtonet client must remain connected with the tunnel running. If either side stops, the public endpoint becomes unavailable.

Operate the project and protect its credentials

Foreground operation

When you run iii compose --up in the foreground, keep that terminal attached while developing or testing. Use the terminal output to identify the first meaningful startup failure. To stop the foreground process, use the normal terminal interrupt and allow the processes to shut down cleanly.

After restarting, verify the console, provider Worker, and application route again. Do not assume that a Worker loaded simply because the engine process remained active.

Docker operation

For a detached Docker deployment, inspect docker compose ps and docker compose logs as part of routine checks. Restart and update containers according to your deployment policy. Before changing an image tag or regenerating assets, preserve reviewed configuration and understand which files the iii generator will leave unchanged.

Protect every credential store

Treat provider API keys, Codex authentication state, GitHub Copilot login state, queue credentials, generated .env files, and the Localtonet device token as secrets. Grant access only to the account or service that needs them. Redact them from support output and rotate them after suspected disclosure.

Keep network exposure narrow

Do not publish the console, SDK WebSocket, REST API, stream API, and every Worker endpoint by default. Determine which route a remote user or system needs, verify that route locally, and expose only its local service.

If the Localtonet client runs on the same machine as iii, a loopback target can reduce unnecessary LAN exposure when the service is configured to listen there. If our client runs on another device or in another container, 127.0.0.1 refers to that device or container itself. In that case, use an address that is reachable from the client and test it from the client's actual network context.

Upgrade using release-specific evidence

Record the current engine and SDK versions before an upgrade. Read the relevant release notes, update in a controlled environment, start the project locally, check Worker and provider registration, test application Triggers, and only then test the public URL. Do not apply an assumed compatibility rule in place of version-specific release guidance.

Troubleshoot installation, providers, console access, and tunnels

iii --version does not run

Check the installer's output, current user, executable permissions, shell startup files, and command search path. Open a new terminal if the installer changed shell configuration. Do not guess the installation directory or add an insecure writable directory to PATH.

Project initialization does not show the expected setup

Confirm that you are using the current iii project init workflow rather than an older versioned quickstart. Record iii --version and compare your installed version with the current repository and applicable release notes. Avoid copying an old iii-config.yaml into a newly generated Compose project unless the matching documentation requires it.

The project starts but reports a missing provider key

Confirm that the selected provider matches the variable in .env. For example, OpenAI API uses OPENAI_API_KEY, Anthropic uses ANTHROPIC_API_KEY, and Kimi uses MOONSHOT_API_KEY. Check for an empty value or a credential stored in a different project directory. Do not print the secret while debugging.

Codex authentication is unavailable

The documented Codex path expects a prior codex login so ~/.codex/auth.json exists. Run the login as the same operating-system account that runs the iii project. If iii runs in a container or under another service account, do not assume that the host user's home-directory login state is visible there.

GitHub Copilot is not ready after adding the Worker

GitHub Copilot uses a subscription and a provider login flow rather than an API key in the documented setup. Confirm that provider-github-copilot was added successfully, then run iii trigger provider::github-copilot::login::start and complete the indicated login process.

llama.cpp cannot connect

Confirm that your own llama server is running and reachable from the iii process. The documented default is http://127.0.0.1:8080, but a container or remote service may require a different reachable address in .env. Test from the same network context as iii.

The no-LLM project has no application endpoint

This is expected for the base template. Current documentation says that the no-LLM template starts only the console. Add a Worker with a user-defined HTTP Trigger before expecting an application route. Do not use the console port as a substitute.

The console fails over HTTPS

The current install page contains both HTTPS and HTTP references for 127.0.0.1:3113. Check the exact startup output from your installed version. The page's final browser instruction uses HTTP. A protocol error can indicate that the listener is plain HTTP rather than TLS, not that the engine has failed.

The console opens but the application request fails

Console availability proves only that the console is reachable. Confirm that the intended Worker started, its Function registered, and its HTTP Trigger uses the method and path in your request. Check required headers, authentication, content type, and body separately.

A Docker port is listening but the route is missing

The generated Docker deployment exposes engine transport ports, but an open port does not create a user-defined Trigger. Inspect Worker startup and Trigger registration. Make sure you are not confusing port 3111 for REST, port 3112 for streams, port 49134 for SDK WebSocket connections, and port 3113 for the console.

Local access works but the Localtonet URL fails

Confirm that our client is connected on the selected device, the tunnel has been started, and the tunnel targets the same local IP address and port that passed the local test. If the client runs in a container or on another machine, verify that the target is reachable from there.

Then request the exact public path with the same method, body, headers, and application credentials. Requesting only the public origin does not test a Trigger registered at a deeper path.

The public URL stops when a terminal closes

The closed terminal may have been running iii compose --up or the Localtonet client. The engine, required Workers, our client, and the tunnel all need to remain active. For unattended operation, use an appropriate supervised deployment rather than leaving an interactive terminal as an accidental production process manager.

The console was exposed publicly by mistake

Stop the tunnel or correct its target immediately. Review what operational data may have been reachable, rotate exposed credentials where necessary, and follow your incident-response process. Restore remote access only after the tunnel points to the intended application service and that service has suitable authorization.

Frequently asked questions

What command installs iii?

The current documentation uses curl -fsSL https://install.iii.dev/iii/main/install.sh | sh. Review remote scripts before execution when required by your security policy.

Is iii --version part of the current two-step install recipe?

It is a sensible verification command, but the current install page proceeds from installation to interactive project initialization. The explicit install-then-version sequence appears in older versioned documentation.

What happens if I select no LLM?

The current no-LLM base template starts only the iii console. It does not create an application HTTP Trigger, so you must add an appropriate Worker and Trigger before expecting a business API route.

Is port 3113 an application HTTP endpoint?

Port 3113 is documented for the iii console. A user-defined application HTTP endpoint comes from an HTTP Trigger and can use a separately configured route or service port.

Should I use HTTP or HTTPS for the local console?

The current install page is inconsistent: it contains both HTTPS and HTTP references for 127.0.0.1:3113. Follow the endpoint printed by your running version. The page's final browser instruction uses http://127.0.0.1:3113.

Where should provider API keys be stored?

Put the required variable in the generated .env file, restrict access to it, and keep it out of version control, screenshots, logs, and documentation. Codex and GitHub Copilot use documented login flows instead of the API-key pattern.

Must iii engine and SDK versions share the same minor version?

This guide does not assert that as a universal rule without current version-specific primary evidence. Record all versions and consult the release notes that apply to the engine and SDK packages you are deploying.

Does one Localtonet HTTP tunnel expose every iii port?

No. An HTTP tunnel forwards to the local IP address and port you configure. If an application requires several iii transport surfaces on one origin, first configure and verify a local reverse proxy, then point the tunnel at that proxy.

Will Localtonet keep iii online after the project stops?

No. The iii project and required Workers must remain active, the Localtonet client must remain connected, and the tunnel must be running. The tunnel forwards traffic but does not replace the local service.

Publish only your verified iii application route

Install iii, complete the interactive provider setup, confirm the local console, and test the actual application HTTP Trigger separately. When that route works and has appropriate authorization, use Localtonet to provide remote access without configuring inbound router port forwarding.

Get Started Free โ†’

Corrections & updates

Substantive changes approved by the Localtonet editorial team are listed transparently below.

Remove the outer article wrapper; move the hero to the beginning and place the guide-navigation card immediately after it; relocate the existing lead figure below the navigation or into a relevant section. Update the installation workflow to reflect the current interactive iii project setup, including the no-LLM path, supported provider choices, required .env or provider-login preparation, startup, and the documented 3113 console behavior. Clearly separate the iii console from user-defined HTTP Trigger routes and explain that the no-L

Localtonet is a secure multi-protocol tunneling and proxy platform designed to expose localhost, devices, private services, and AI agents to the public internet supporting HTTP/HTTPS tunnels, TCP/UDP forwarding, mobile proxy infrastructure, file server publishing, latency-optimized game connectivity, and developer-ready AI agent endpoint exposure from a single unified control plane.

support