26 min read

Self-Host a Multi-GPU ggrun API with Localtonet

Install and verify a multi-GPU ggrun inference server, then expose its llama.cpp HTTP API securely through a Localtonet tunnel.

Multi-GPU workstation serving a local inference API through a secure tunnel to a remote client.
ggrun runs inference on the local multi-GPU host while Localtonet carries remote API traffic through a tunnel.
Self-Hosted AI ยท ggrun ยท Multi-GPU ยท Localtonet ยท 2026

Run large GGUF models on your own hardware, verify the local API, and make it reachable through a controlled public endpoint

ggrun is a launcher for llama.cpp and ik_llama.cpp that calculates model placement across available VRAM, system RAM, and multiple GPUs before starting an inference server. This guide covers installation on Linux, macOS, and Windows, launching a local GGUF model, using ggrun's download workflow, checking the generated multi-GPU plan, and verifying the HTTP service without assuming a default port. After the local server works, we show how to expose its configured host and port through a Localtonet HTTP tunnel. Security and troubleshooting sections explain how to avoid accidentally publishing an unrestricted inference endpoint.

๐Ÿ”’ Verify and restrict the API before public exposure ๐ŸŒ OpenAI-compatible HTTP API through Localtonet โšก Placement planning for mismatched multi-GPU systems

How ggrun, llama.cpp, and Localtonet fit together

Architecture showing a remote client reaching the ggrun llama.cpp API through a Localtonet tunnel.
The public endpoint forwards requests through the outbound Localtonet tunnel to the API running on the private host.

The first distinction to understand is that ggrun is not itself an inference engine. It examines a GGUF model and the available machine resources, creates a launch plan for a supported backend, checks whether the planned model placement, KV cache, and safety headroom fit, and then starts the backend server. The actual inference process is provided by llama.cpp or ik_llama.cpp.

This separation is useful on systems with several GPUs that do not have identical memory capacities or transfer characteristics. Manually deciding how to divide a large mixture-of-experts model across mismatched cards can require repeated trial and error. ggrun is designed to calculate that placement using the GGUF tensor layout, available VRAM and RAM, per-GPU bandwidth information, and backend capabilities. It supports dense and mixture-of-experts models across single-GPU, multi-GPU, CPU, and RAM-offload configurations.

Once the backend starts, it exposes the llama.cpp HTTP service at the host and port selected for that launch. This includes the OpenAI-compatible /v1 API for chat completions and completions. ggrun also supports the Anthropic-compatible endpoint used by its local Claude Code workflow, but the exact endpoint URL should be taken from the running configuration rather than guessed.

Localtonet enters the workflow only after that local HTTP service has been installed, started, and tested. Our client establishes an outbound connection to a Localtonet relay, so the machine does not require an inbound router port-forwarding rule, a public IP address, firewall changes for inbound internet traffic, or a separate VPN setup. An HTTP tunnel then maps its public address to the ggrun server's actual local IP address and port.

๐Ÿง  Placement planning ggrun reads the GGUF layout and machine resources, checks memory requirements, and prepares a launch plan for the selected inference backend.
๐Ÿ–ฅ๏ธ Mismatched GPU support The planner is intended for single-GPU and multi-GPU systems, including rigs where cards differ in VRAM capacity or bandwidth.
๐Ÿ” Inspectable command ggrun keeps the generated backend command visible, making it easier to understand and reproduce the selected launch configuration.
๐Ÿ”Œ Compatible HTTP API The resulting llama.cpp server provides an OpenAI-compatible /v1 API at the configured host and port.
๐ŸŒ Outbound remote connectivity With Localtonet, the host running the client opens the relay connection outbound and receives a public HTTP address for the selected local target.
๐Ÿ›‘ Explicit lifecycle Creating a Localtonet tunnel does not start it automatically. The selected device must remain connected, and the tunnel must be running for remote requests to work.

Prerequisites and planning decisions

Before installing anything, identify the machine that will run inference. It needs enough aggregate VRAM and system RAM for the chosen GGUF, its context requirements, the KV cache, and operational headroom. ggrun performs fit checks, but it cannot make an oversized model fit resources that are not physically available. CPU or RAM offload can broaden the configurations that are possible, although the resulting performance depends on the model and hardware.

The available project evidence does not define universal minimum GPU models, driver versions, operating-system versions, RAM capacities, or disk-space requirements. Those values depend on the backend, acceleration stack, model architecture, quantization, context length, and hardware. Confirm that llama.cpp or ik_llama.cpp supports your intended acceleration environment before treating the system as production-ready.

Requirement Why it matters What to confirm
Linux, macOS, or Windows host ggrun provides documented installer paths for these platforms. Use a shell with the tools required by the corresponding installer and open a new terminal after installation if PATH changes were made.
Supported inference hardware The backend must be able to execute the selected model. Confirm backend support for your CPU, GPUs, drivers, and acceleration environment.
Enough VRAM, RAM, and storage Model weights, cache allocation, runtime headroom, and downloaded GGUF files all consume resources. Choose a quantization and context target appropriate for the actual machine.
A GGUF model source ggrun needs either a local GGUF or a model repository it can download. Review the model's license, file size, architecture, and hardware requirements.
A known HTTP host and port API clients and the Localtonet tunnel need an exact destination. Record the host and port shown or configured for the successful launch. Do not assume a default.
Localtonet client access The client must run on a device that can reach the inference server. Install the current client, keep its device token private, and select an available relay from the current dashboard.
Review remote installation scripts before running them

The quick-install commands below download and execute project-maintained scripts. This is convenient, but it also trusts the content delivered from the referenced branch at execution time. For a controlled environment, inspect the script first, choose the project's documented release or source controls, and follow your organization's software-review process.

Plan the network boundary before launching the server. If ggrun and the Localtonet client run on the same machine, a loopback listener can be sufficient because our client can target that local address. If the Localtonet client runs on a different device, the inference server must listen on an address reachable from that device, and the host firewall must allow only the necessary local path. Do not broaden the LAN listener merely because remote access will eventually be required.

Install ggrun on Linux, macOS, or Windows

ggrun provides a combined setup script for Linux and macOS and a PowerShell installer for Windows. The commands below are the documented quick-start paths. The project notes that these commands retrieve installers from the main branch. Windows normally installs the latest published launcher, while Linux setup can build the current source when Go is available. A successful check on the main branch does not necessarily mean every change has already reached the latest packaged release.

Linux and macOS installation

curl -fsSL https://raw.githubusercontent.com/raketenkater/ggrun/main/setup.sh | bash

Let the setup finish, read any messages it prints, and open a new terminal if the installer added ggrun to PATH. If you want to start it immediately from the documented standard installation location without reopening the shell, use:

~/ggrun/ggrun

The combined script handles the platform-specific setup path. The available evidence does not establish every package it may install or every prompt it may display on every distribution and macOS version, so review the installer output rather than assuming a silent, identical result across systems.

Windows PowerShell installation

$s = irm https://raw.githubusercontent.com/raketenkater/ggrun/main/install.ps1
iex "& { $s }"

This form loads the script text and invokes it as a script block. It is intentionally different from a simple pipeline into iex. The project adopted this approach to avoid problems caused by leading characters such as a UTF-8 byte-order mark and to handle installer behavior when standard input is redirected.

Open a new PowerShell terminal after installation so a newly added PATH entry is available. To run the launcher immediately from its documented standard location, use:

& "$env:USERPROFILE\ggrun\ggrun.cmd"

Confirm that the launcher is available

In a new terminal, run ggrun without a model argument:

ggrun

This opens the project's terminal interface. Reaching that interface confirms that the launcher is callable, but it does not yet prove that a model can load, that the backend can access every GPU, or that an HTTP listener has started. Complete those checks separately.

Release and source installations are not identical

The quick installers retrieve setup logic from the main branch, while the installed launcher or build path can depend on the platform and available tools. If you require a reproducible version, use the release and source controls documented by the ggrun project rather than assuming that every fresh installation resolves to the same commit.

Prepare ggrun for a multi-GPU machine

GGUF model layers distributed across multiple GPUs and exposed through one local API.
ggrun and llama.cpp can divide model layers across available GPU memory while serving a single API.

ggrun can derive placement information from the detected hardware, but NVIDIA users can also measure host-memory and pinned PCIe transfer bandwidth. The project recommends running the bandwidth detection once and repeating it after GPUs or card slots change:

ggrun detect --bandwidth

The resulting cached profile is tied to the CPU, RAM, GPU, and PCI layout that produced it. ggrun accepts it only when the current hardware matches that layout. If the profile is missing, stale, incomplete, or corrupt, normal detection and launches fall back to derived PCIe values rather than trusting unsuitable measurements.

This hardware matching matters on mixed rigs. Two cards with similar compute capability can still have different memory capacity, slot connectivity, or transfer behavior. A placement that fits mathematically may not be the best stable option for the requested context and workload. ggrun uses the information available to create a plan, check memory requirements, and keep the generated command visible for inspection.

The launcher also supports bounded tuning and comparison workflows. Its --ai-tune capability measures a bounded set of safe performance options and preserves the winning configuration for the same setup. Eligible direct or terminal-interface launches can perform a limited automatic comparison between a stable baseline and one high-confidence finalist.

Treat these measurements as configuration evidence, not as a universal benchmark. The project's synthetic serving workload uses repeated cache-backed turns and mixed prefill and decode activity. It does not prove maximum hardware throughput or predict the performance of every coding agent, prompt distribution, context length, or concurrent workload. Production evaluation should use your own representative requests.

Do not change several capacity variables at once

Model quantization, context length, cache requirements, GPU placement, CPU offload, and concurrency can all affect memory pressure. Begin with a configuration that passes ggrun's fit checks and completes an actual inference request. Change one major variable at a time so an out-of-memory failure or slowdown can be traced to a specific adjustment.

Launch a local GGUF or download a model

After installation and hardware detection, choose one of three supported starting points: supply a local GGUF file, ask ggrun to download a model repository, or open the terminal interface and select the workflow there. The most direct local-file command is:

ggrun model.gguf

Run this from the directory containing the file, or replace model.gguf with the correct path to your own GGUF. Do not copy a placeholder path unchanged. ggrun reads the model, evaluates its tensor layout against the machine, builds the launch plan, checks whether the model and cache allocation fit with safety headroom, and starts the selected backend when the plan is accepted.

To download through ggrun and then launch, the project provides this form:

ggrun unsloth/Qwen3.6-27B-GGUF --download

The same documented example can be expressed as a download operation:

ggrun download unsloth/Qwen3.6-27B-GGUF

That repository name is an example, not a recommendation that the model will fit every system. Confirm its current files, quantizations, disk requirements, model license, and runtime compatibility before downloading it. Large repositories may contain multiple GGUF variants with substantially different resource needs.

Running ggrun without an argument opens the terminal interface, where model downloads, recommendations, launch planning, and generated commands are available in one flow. This is useful when exploring a machine for the first time because it keeps the selected settings and resulting backend command visible.

ggrun passes unknown flags through to llama-server. That makes backend-level customization possible, but it does not make arbitrary flags safe or portable. Only use flags documented for the exact backend version that ggrun launches. This article deliberately does not prescribe host, port, authentication, context, or cache flags because the supplied project evidence does not establish one universal set for every supported backend and installation.

Record the listener selected for the launch

Watch the launch output and generated command. Record the exact HTTP host and port that the server reports or that you explicitly configured. The ggrun documentation excerpt supplied for this guide does not establish a default port, so a hardcoded example would be unreliable.

In the remaining examples, PORT means the real numeric port from your successful launch. Replace it before running a command. If the server is not listening on 127.0.0.1, replace that address with the actual locally reachable address as well.

Verify the inference service locally first

Remote tunneling should never be the first test of an inference server. A local verification separates model-loading and backend problems from tunnel configuration problems. Keep the ggrun process running and confirm four layers in order: process startup, resource fit, HTTP connectivity, and an actual inference response.

1

Confirm that model loading completed

Read the ggrun and backend output. The process should remain running after the model and cache are initialized. If it exits, reports an out-of-memory condition, or never reaches the server-start stage, resolve that issue before testing HTTP.

2

Record the real host and port

Use the listener shown in the launch output or generated command. Do not assume a port from another llama.cpp tutorial because ggrun uses the configured host and port for the current launch.

3

Test basic HTTP connectivity

Send a verbose request to the listener from the inference host. An HTTP response confirms that a service accepted the connection, even if the root path returns a non-success status. A refusal or timeout means the listener, address, port, process, or local policy still needs attention.

4

Run a real API request with a compatible client

Configure an OpenAI-compatible client to use http://HOST:PORT/v1, then send a small completion or chat-completion request. This proves more than a socket test because it exercises request parsing, model inference, and response generation.

5

Observe resource use and stability

Check that the request completes without a backend crash or memory error. Repeat with a workload representative of your intended context and concurrency before allowing remote users to depend on the service.

On Linux or macOS, replace PORT with the actual value:

curl -v http://127.0.0.1:PORT/

In Windows PowerShell, calling curl.exe explicitly avoids ambiguity with shell aliases:

curl.exe -v http://127.0.0.1:PORT/

The purpose of this request is transport verification. The root path's response body and status can vary with the backend build and configuration. A valid HTTP response proves that the listener accepted the request, while a successful OpenAI-compatible API call is the stronger functional check.

Use the API base URL your application expects

ggrun exposes llama.cpp's OpenAI-compatible interface under /v1. Many compatible applications ask for a base URL, while others ask for a full endpoint. Read the client's field description carefully and avoid appending /v1 twice.

Expose the working ggrun API with Localtonet

Once local inference works, an HTTP tunnel is the appropriate Localtonet tunnel family because the service is an HTTP API. The client must run on the inference host or on another device that can reach the recorded listener. If the server listens only on loopback, run our client on the same machine rather than opening the service across the LAN unnecessarily.

HTTP tunnel process types can use a random subdomain, a custom subdomain where supported, or a custom domain. Each serves the configured content at a public HTTPS address. Availability can vary by plan or current dashboard options, and custom-domain DNS requirements should be checked against current documentation rather than inferred.

1

Install and run the Localtonet client

Install the current Localtonet application on the device that can reach the ggrun HTTP listener. Keep the client running because the public endpoint is available only while the selected device is connected and the tunnel is active.

2

Authenticate or select the device

Use the device-specific authentication token supplied through your Localtonet account or select the corresponding connected device in the dashboard. Never place the token in application code, screenshots, logs, or this API's public configuration.

3

Select an available relay server

Choose from the server or region values currently available in the product. Do not copy a server code from an unrelated tutorial because availability can vary by account, plan, region, or product version.

4

Create an HTTP tunnel for the local listener

Select an HTTP tunnel and enter the exact local IP address and port verified earlier. If Localtonet and ggrun share a host, this can be the verified loopback target. If they run on separate devices, use the inference host address reachable from the client device.

5

Start the tunnel

Creating the configuration is not enough. Use the Start action and wait for the tunnel to become active. The dashboard then provides the public address assigned to the running HTTP tunnel.

6

Test through the public URL

Repeat the connectivity and functional API checks through the assigned public address. For an OpenAI-compatible client, use the public URL in the same place where you previously used the local base address, preserving the required /v1 path exactly once.

For the current dashboard workflow and fields, consult our Localtonet HTTP tunnel documentation. Exact client commands, region codes, and domain DNS values are intentionally not hardcoded here because they must come from the current application and account configuration.

A public URL changes the threat model

Treat the assigned address as internet-reachable. Do not assume that an obscure URL is an access-control mechanism. Before sharing it, determine what authentication and authorization are enforced by the API or an approved protective layer, limit who receives the address, and stop the tunnel when public access is no longer required.

Secure a remotely reachable inference API

A self-hosted model can still expose sensitive capabilities. An unrestricted API may allow unknown users to consume GPU time, fill context windows, trigger large downloads through surrounding automation, submit confidential prompts, or interfere with workloads expected by local users. If the model is connected to an agent with tools, files, repositories, or credentials, the consequences can extend beyond inference capacity.

The supplied ggrun evidence establishes compatible API exposure, but it does not establish a universal authentication default. Therefore, do not assume that every generated server configuration requires credentials. Verify the effective behavior of your exact backend and launch command before starting a public tunnel.

Apply security in layers

  • Bind narrowly: when the Localtonet client runs on the inference host, prefer a local listener over a broadly reachable LAN listener unless another local requirement needs network access.
  • Require authorization: use authentication supported by the exact server build or place an approved authenticated application layer in front of it. Do not invent or rely on an undocumented ggrun credential option.
  • Use least privilege: an application consuming the API should not automatically receive unrelated filesystem, shell, repository, or cloud credentials.
  • Separate model inference from agent tools: publishing an inference endpoint does not require exposing every tool or local service used by an agent.
  • Control distribution: share the public URL only with intended users and avoid placing it in public repositories, screenshots, client-side code, or searchable logs.
  • Stop unused access: stop or delete the Localtonet tunnel after the remote session or integration no longer needs it.
  • Protect device tokens: Localtonet authentication tokens identify client devices. Store them as secrets and rotate or replace them through the supported account workflow if exposed.
  • Review model licenses and data handling: self-hosting changes where inference occurs, but it does not remove model-license obligations or the need to handle prompts and outputs appropriately.

If an application requires browser access from another origin, it may also have cross-origin request requirements. Do not enable permissive cross-origin behavior by default. Configure only the origins and methods needed by the intended client, using options supported by the actual backend or protective application layer.

Routine operation and maintenance

A reliable setup has two separate lifecycles. The inference lifecycle includes model availability, ggrun planning, backend startup, memory allocation, and request processing. The connectivity lifecycle includes the Localtonet client, device connection, relay selection, tunnel state, and public URL. Monitoring these independently makes failures easier to isolate.

Starting a session

  1. Confirm that the expected GPUs, RAM, and model storage are available.
  2. Run bandwidth detection again if the NVIDIA GPU or slot layout has changed.
  3. Launch the chosen GGUF through ggrun.
  4. Wait for loading to finish and verify a local API request.
  5. Start the Localtonet client and then the existing HTTP tunnel.
  6. Verify the public endpoint with a small request before handing it to an application.

Stopping a session

Stop incoming remote use first so new work is not submitted while the inference process is shutting down. Stop the Localtonet tunnel, then end the ggrun-managed server using the normal terminal or service-management mechanism for the way it was launched. If you run ggrun under an operating-system service manager, use that manager rather than starting a duplicate interactive process.

Changing models or listener settings

A new model can produce a different placement and memory requirement. Verify it locally again before reusing the public endpoint. If the local host or port changes, update the Localtonet HTTP target and retest. A tunnel still pointing to an old listener may remain configured while returning connection failures.

Evaluating performance

Separate startup success from acceptable application performance. A model may fit safely but still respond too slowly for an interactive workload. Test the prompt sizes, context lengths, request concurrency, and agent behavior you actually intend to run. ggrun's bounded measurements and saved evidence help compare safe configurations, but synthetic measurements do not replace application-level testing.

Troubleshooting the complete workflow

Diagnostic flow checking GPUs, model loading, local API, tunnel, and remote access in order.
Testing each layer in sequence isolates failures before moving from local inference to remote access.
Symptom Likely layer What to check
ggrun is not found Installation or PATH Open a new terminal. On Linux, try the documented ~/ggrun/ggrun path. On Windows, try $env:USERPROFILE\ggrun\ggrun.cmd.
The installer fails or behaves unexpectedly Installer environment Inspect the downloaded script, confirm the required shell and network access, and use the project's controlled release or source installation path when reproducibility is required.
The model will not fit Model and memory planning Review the chosen GGUF quantization, context and cache needs, available VRAM and RAM, and ggrun's fit output. Choose a configuration appropriate for the actual hardware.
GPU placement looks outdated after a hardware change Hardware profile Run ggrun detect --bandwidth again on NVIDIA hardware. Cached measurements are accepted only for the matching CPU, RAM, GPU, and PCI layout.
The model loads, but local HTTP is refused Backend listener Confirm that the process is still running and use the host and port shown by the current launch. Do not assume a default port.
The root URL returns an error status HTTP routing A response can still prove transport connectivity. Perform a real request through the documented OpenAI-compatible /v1 API to verify inference.
Local API works, but the tunnel does not Localtonet target or lifecycle Confirm the client device is connected, the tunnel was explicitly started, and the configured local IP and port match the verified listener.
The tunnel works only when both services share a host LAN reachability If the client moves to another device, loopback no longer points to the inference machine. Use a deliberately configured LAN-reachable listener and restrict local firewall access appropriately.
The client reports an invalid API path Base URL configuration Check whether the client expects the server origin, the /v1 base, or a full operation path. Avoid adding /v1 twice.
Requests are slow despite successful startup Workload performance Test representative prompts and concurrency, review placement and offload behavior, and compare controlled configurations rather than relying only on startup success.
Use a layer-by-layer test order

First prove that ggrun starts the backend. Next prove local HTTP connectivity. Then prove one local inference request. Only after those checks should you test the Localtonet public URL. This order prevents a model-loading problem from being misdiagnosed as a tunnel failure.

Frequently asked questions

Is ggrun an alternative inference engine to llama.cpp?

No. ggrun is a launcher around llama.cpp and ik_llama.cpp. It analyzes the GGUF and machine, builds and checks a placement plan, starts the selected backend, and keeps the generated command visible. The backend performs inference and serves the HTTP API.

Does ggrun require identical GPUs?

No. A central ggrun use case is planning large-model placement across mismatched multi-GPU systems. It considers available VRAM, RAM, bandwidth information, the GGUF tensor layout, and backend capabilities. A successful plan still depends on the total resources being sufficient for the selected model and runtime settings.

What port does the ggrun API use?

This guide does not assume a default because the supplied project evidence identifies the API as running at the configured host and port without establishing one universal port. Use the listener shown in the launch output or generated command, verify it locally, and enter that same value as the Localtonet target.

Can an OpenAI-compatible application connect to the server?

Yes. The launched llama.cpp server exposes an OpenAI-compatible /v1 API for chat completions and completions. Configure the application's base URL using the verified local address during initial testing and the Localtonet public address after the tunnel is active.

Does Localtonet require router port forwarding?

No. Our client establishes an outbound connection to a Localtonet relay. That allows the tunnel to provide a public URL without requiring inbound router port forwarding, a public IP address, inbound firewall changes, or a separate VPN setup.

Should the inference server listen on every network interface?

Not when that exposure is unnecessary. If the Localtonet client runs on the same machine, it can target a local listener. A LAN-reachable binding is needed only when the client runs elsewhere or another approved local consumer requires it. In that case, restrict firewall access to the necessary devices.

Is the public tunnel available after it is created?

Not automatically. Creating a Localtonet tunnel and running it are separate lifecycle actions. The selected client device must be connected, and the tunnel must be started. It becomes unavailable when the client disconnects or the tunnel is stopped or deleted.

Does a public HTTPS address automatically protect the inference API from unauthorized use?

No. A public HTTPS address provides an HTTP access path, but it should not be treated as proof of application authorization. Verify authentication and access-control behavior for the exact backend or protective application layer, keep credentials private, and stop the tunnel when remote access is not needed.

Connect your verified ggrun API with Localtonet

Start the model locally, confirm the configured HTTP listener and a real inference request, then create a Localtonet HTTP tunnel for that exact host and port. Keep the device token private and apply authentication before sharing the public endpoint.

Get Started Free โ†’

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