
Find out whether your model, machine, API, stream, or network is making remote inference slow
A slow remote LLM endpoint does not automatically mean the tunnel is slow. Model loading, memory pressure, prompt processing, token generation, concurrent requests, response buffering, and internet latency can each produce a different kind of delay. In this guide, we show how to isolate those layers by verifying the local API first and then comparing equivalent local and tunneled requests. We also explain what a Localtonet HTTP tunnel can solve, what it cannot change, and how to expose a self-hosted AI API without overlooking basic access controls.
๐ What's in this guide
Start with the complete request path
When a local LLM is used remotely, a single visible pause may contain several unrelated delays. The remote client must resolve and connect to the public endpoint, send an HTTP request, wait for the application to accept it, wait for the model runtime to process the prompt, and then receive generated tokens. If any intermediary buffers the response, the client may also wait even though the model has already started generating.
The fastest troubleshooting method is therefore not random configuration tuning. It is controlled isolation. First prove that the model API works correctly from the machine running it. Then measure its behavior under a stable local workload. Only after that should you send the same workload through the public Localtonet URL. This establishes whether the extra delay appears before inference, during inference, while streaming the response, or only across the remote network path.
Avoid reducing performance to one total response-time number. A request that takes 30 seconds because no output appears for 25 seconds has a different bottleneck from one that starts in one second but generates slowly for the remaining 29 seconds. Both feel slow, but they require different fixes.
| Measurement | What it represents | Likely influences |
|---|---|---|
| Model load time | Time required to make the model ready for requests | Model size, storage speed, available memory, runtime initialization |
| Time to first token | Delay between submitting the prompt and receiving initial generated content | Queueing, prompt processing, cold start, prompt length, network setup |
| Generation rate | How quickly subsequent output tokens arrive | Model architecture, quantization, hardware, runtime, contention |
| Total request time | End-to-end duration until the response completes | All inference and transport stages plus output length |
| Remote overhead | Difference between controlled local and tunneled tests | Network path, connection setup, request transfer, buffering, client behavior |
Build a trustworthy local baseline first
Do not begin by changing the tunnel, model, quantization, context limit, and client at the same time. If several variables change, an improvement or regression cannot be attributed to a particular cause. Establish a reproducible local test while the remote endpoint is out of the measurement path.
Run the test from the same machine as the inference service when possible. Address the service using the local host or local network address that actually corresponds to its configured listener. The exact port, API path, request schema, and model name depend on your runtime and configuration. Use values reported by that runtime rather than copying an assumed default.
Define a fixed test request
Choose one model, one prompt, one generation configuration, and one maximum output length. Keep them unchanged across local and remote trials. A short factual prompt is useful for connectivity checks, but it is not enough to characterize a large-context workload. Maintain at least two fixtures:
- A small prompt that reveals connection, queue, cold-start, and basic streaming behavior.
- A representative prompt similar in size and structure to the workload users actually submit.
Save the exact request body instead of retyping it. If the API supports a deterministic generation setting, it may help reduce variation, but only use settings documented by the runtime. Even with identical parameters, some inference systems and models can produce variation, so focus on timing ranges rather than demanding identical output text.
Record more than elapsed time
For every trial, record whether the model was already loaded, the prompt size, requested output limit, HTTP status, time to first response content, completion time, and whether output streamed incrementally. Also note CPU, GPU, accelerator, memory, and storage activity using the monitoring tools available on the host operating system.
Run multiple trials. The first request may include model loading or initialization work that later requests do not. Label cold and warm measurements separately instead of averaging them into one misleading number. If a warm local request is consistently slow, introducing a public URL will not solve the underlying inference bottleneck.
A web interface can add rendering, conversation history, browser extensions, client-side parsing, or its own proxy layer. Test the inference API directly first. Once the API is understood, test the user interface as a separate layer.
Verify basic local correctness
A valid local test should confirm that the process is listening on the expected address and port, the requested model exists and can be loaded, the API accepts the request format, and the response contains the expected HTTP status and content type. A connection refusal means there is no reachable listener at that address. An HTTP error means a listener answered, so inspect the response body and server logs instead of treating it as a network outage.
Binding behavior matters. A service listening only on the loopback interface can still be reached by a Localtonet client running on the same device when the tunnel target is configured accordingly. If the Localtonet client runs on another device, that device must be able to reach the chosen local IP and port. Do not broaden the model server's LAN exposure unless the architecture requires it and the network is appropriately protected.
Diagnose model-loading and context-memory pressure

Local inference has a hard capacity boundary. The runtime needs memory for model weights, execution overhead, and request state. Context processing and concurrent sessions can require additional memory. The exact amount is model-specific and runtime-specific, so a parameter count or model file size should not be treated as a complete memory requirement.
A Localtonet HTTP tunnel carries requests to the existing local service. It does not resize the model, add host memory, change the inference runtime, or provide compute for local generation. If the model fails locally because it cannot fit or the process is being terminated under memory pressure, the same service will fail through the public endpoint.
Recognize model-capacity failures
Typical evidence includes an explicit allocation error, a process that exits while loading, an operating-system termination event, repeated runtime restarts, or a model that never reaches a ready state. Storage activity may remain high if the system is repeatedly reading model data while memory remains exhausted. Check the runtime log and the operating system's process or event log rather than inferring the cause from a remote timeout.
If the model loads but responsiveness collapses, observe whether the host begins paging or swapping heavily. Moving active data between memory and storage is much slower than keeping it in the memory used by the inference device. A request can therefore remain technically alive while becoming unsuitable for interactive use.
Separate model weights from context growth
A model may load successfully with a short prompt and fail only on long conversations. That pattern points away from base model loading and toward context-related memory or compute pressure. Conversation applications often resend prior messages as part of each new request, so a chat that began quickly can become progressively slower.
Compare a fresh conversation with a long conversation using the same current question. Inspect the actual request payload rather than the text visible in the chat box. System instructions, tool definitions, retrieved documents, structured schemas, and conversation history may make the submitted context much larger than the user realizes.
Use controlled reductions to confirm the diagnosis
Reduce one factor at a time. Try a shorter prompt, a smaller supported context setting, fewer simultaneous requests, or a smaller or more aggressively quantized model that your runtime officially supports. If the issue disappears after one controlled reduction, repeat the test to confirm the relationship. Do not assume that a quantized model has a universal memory footprint across runtimes or formats.
A longer timeout can help a legitimately slow batch request finish, but it cannot correct failed allocation, process termination, or severe paging. Confirm that the model is healthy locally before increasing any timeout.
Measure prompt processing and token generation separately

Inference has at least two user-visible phases. During prompt processing, the model consumes the supplied context before returning generated content. During generation, it produces subsequent tokens. Hardware and runtime behavior can affect these phases differently, which is why one overall duration is insufficient.
Investigate high time to first token
If the connection succeeds quickly but the first generated content arrives late, look at queueing, cold model loading, and prompt size. Repeat the same request after the model is warm. If only the first trial is slow, initialization is a likely contributor. If every trial becomes slower as the prompt grows, prompt processing or context-related memory pressure deserves attention.
Compare prompts at several controlled sizes while keeping the requested output length constant. The precise token count should come from the tokenizer or request metrics supplied by the model stack when available. Character count is only an approximation because tokenization varies by language, content, and model.
Investigate slow output after the first token
If the first token arrives promptly but text continues slowly, measure the generation rate. Count generated tokens using the model's tokenizer or runtime metrics if available, then divide by the generation interval rather than total request time. Including prompt processing in this calculation understates the subsequent-token rate.
Slow generation can reflect a model that is too demanding for the available hardware, an inefficient or unsuitable runtime configuration, thermal or power constraints, background contention, or concurrent inference. Compare results with the host otherwise idle. Then introduce normal background workloads one at a time.
Control output length
Total duration naturally rises when the model generates more output. A remote request that produces a much longer answer is not comparable to a local request that stops early. Fix the output limit and record the actual generated amount. Also distinguish a stop caused by a normal end condition from a client timeout or broken connection.
| Observed pattern | Most useful next test | Interpretation |
|---|---|---|
| First request slow, warm requests fast | Restart the runtime and repeat a labeled cold test | Model loading or initialization is contributing |
| Long prompts delay the first token | Compare fixed output with several prompt sizes | Prompt processing or context pressure is contributing |
| First token fast, later tokens slow | Measure generation rate while the host is idle | Decode performance is likely the main bottleneck |
| Long chats degrade over time | Compare the full payload with a fresh conversation | Accumulated history or attached context is likely involved |
| Local and remote generation rates match | Inspect first-byte delay and client rendering | The core inference rate is probably not a tunnel issue |
Test queues and concurrent requests deliberately

A single successful request proves basic function, not multi-user capacity. When several clients submit prompts, the inference runtime may serialize requests, batch parts of them, or process them concurrently. The behavior depends on the server and its configuration. Do not assume that an HTTP service handles parallel inference simply because it accepts parallel network connections.
Start with one warm request and establish a stable range. Then test two simultaneous requests using the same controlled fixture. Increase only as needed to reproduce the real workload. For each request, record submission time, first-token time, completion time, status, and whether the server queued it.
If the first request remains normal while the second waits without consuming substantial inference resources, the server may be serializing work. If both begin but each generates more slowly, they may be sharing compute or memory bandwidth. If the process becomes unstable, aggregate memory demand may exceed the safe capacity.
Watch for hidden concurrency
Automated retries, health checks, chat interfaces, agent loops, and multiple browser tabs can create traffic that is not obvious from a single foreground client. A retry can be especially damaging when the original request is still running, because one perceived timeout becomes two expensive inference jobs.
Correlate application request logs with process utilization. If the server supports request identifiers, use them without placing credentials or private prompt content into logs. Record enough metadata to identify overlap while applying appropriate retention and access controls.
Choose an overload policy
For an interactive endpoint, predictable queueing or explicit rejection can be better than accepting unlimited work and making every session unusable. The exact queue, worker, batch, and concurrency controls must come from the model server's documented configuration. We cannot prescribe a universal setting because different runtimes manage model processes and accelerators differently.
Load-test cautiously. LLM requests consume far more resources than ordinary health checks. Begin below the expected capacity, use bounded output, avoid sensitive prompts, and stop if memory usage or system stability deteriorates.
Determine whether streaming is real or buffered

Streaming improves perceived responsiveness because the user can read output before generation completes. It does not make the model generate tokens faster. It changes when generated data becomes visible.
A streaming API normally keeps the HTTP response open and sends incremental events or chunks. The exact framing might be server-sent events, line-delimited data, or another documented format. The client must request the correct mode, the server must emit incremental content, and every layer between them must pass that content without waiting for the complete body.
Test the local stream first
Use a client that displays response bytes as they arrive. Some command-line and programming clients buffer output unless configured otherwise, and some API testing interfaces render only after completion. Confirm streaming with timestamps for the first event and several later events. A changing progress indicator alone does not prove that model tokens are arriving incrementally.
If the local API returns the entire response at once, investigate the runtime and request settings before examining the tunnel. If local streaming works but the remote client receives a burst at the end, compare the public request with a simple streaming-capable client. This separates transport behavior from browser or application rendering.
Distinguish generation gaps from buffering
Streaming is not always perfectly uniform. Token production can pause because of tool execution, model behavior, batching, queue transitions, or application processing. Capture arrival timestamps for chunks. If many chunks share nearly the same arrival time after a long silence, buffering is plausible. If chunks arrive continuously but slowly, the model's generation rate is the more likely limitation.
A streamed response can feel faster because the first content appears earlier, but model memory requirements and total inference work remain. Measure first-token delay, generation rate, and completion time independently.
Compare local inference with a Localtonet HTTP tunnel

Once the local API is healthy and measured, add remote access as a distinct layer. The Localtonet client on a device that can reach the model service establishes an outbound connection to our relay server. This provides a public endpoint without requiring inbound router port forwarding, firewall changes, VPN setup, or a public IP address.
HTTP tunnels point to a local IP address and port. HTTP and File Server tunnels can use Random Sub Domain, Custom Sub Domain, or Custom Domain process types, with availability and related configuration depending on the current product options. Each serves content at a public HTTPS address. Exact server or region values must be selected from the current dashboard rather than copied from an article.
Configure the tunnel using the documented lifecycle
Install and run the Localtonet client
Run the client on the model host or another device that can reach the model API at its configured local IP address and port. Use the current installation instructions for the operating system rather than an unverified command copied from an older guide.
Authenticate or select the client device
Use the device-specific authentication token associated with the client that will carry the tunnel. Treat this token as a credential. Do not place it in source code, screenshots, logs, prompts, or test fixtures.
Select an available relay server
Choose from the server or region values currently presented by the Localtonet dashboard. Availability can change by product configuration, so do not hardcode a server code from an unrelated setup.
Create the HTTP tunnel configuration
Enter the local IP address and port where the verified LLM HTTP API is listening. Select the appropriate HTTP process type from the options currently available to your account. The tunnel target must match the endpoint proven during local testing.
Start the tunnel
Creating a tunnel does not make it active. Use the Start button and confirm that the selected Localtonet client remains connected. The public URL works only while that client is connected and the tunnel is running.
Use, stop, or delete the tunnel
Test the assigned public HTTPS URL using the same API path and request body used for the local baseline. When remote access is no longer required, stop the tunnel. Delete configurations that are no longer needed.
For the current interface and documented HTTP workflow, consult the Localtonet HTTP tunnel documentation. The article intentionally does not provide a guessed client command, relay code, port, or custom-domain DNS record because these details must come from the current product and your actual model service configuration.
Run a fair local-versus-remote comparison
Send the same saved request to the local URL and the Localtonet public URL. Keep the model warm, use the same client if possible, and avoid overlapping requests. Compare HTTP status, first-token delay, generation rate, total duration, output amount, and streaming pattern.
Repeat the remote test from more than one network if the problem is location-specific. For example, compare the host network, another fixed connection, and the actual remote user's connection. A single remote measurement can be distorted by wireless congestion, mobile-network variation, packet loss, or the client's own load.
Interpret the difference carefully. If local and tunneled time to first token and generation behavior are similar, the tunnel is probably not the dominant bottleneck. If local inference is healthy but the public request cannot connect, inspect the Localtonet client state, tunnel state, and target address. If the public connection succeeds but the application returns an error, inspect the model API logs and request format.
A Localtonet configuration must be started, and the selected client device must remain connected. Also verify that the client device can reach the configured local target. A running model on a different machine is not reachable merely because both devices are online.
Protect a remotely reachable LLM API
A model API intended only for local development may have no authentication, permissive cross-origin behavior, broad administrative operations, or access to sensitive tools. Giving it a public URL changes its exposure even when the underlying model remains on your machine.
Put authentication and authorization in front of the endpoint when the application does not provide suitable controls itself. The exact mechanism depends on your architecture and must be supported by the application or a properly configured gateway. Do not embed long-lived credentials in browser code, public repositories, command history, screenshots, or prompt text.
Apply least privilege. If remote clients only need generation, do not expose model-management, file-management, shell, plugin-administration, or unrestricted tool-execution routes. Where your application or access layer supports IP restrictions, request limits, endpoint allowlists, or role-based access, use them according to the actual user population.
Treat prompts and outputs as potentially sensitive data. Avoid logging complete request bodies by default, particularly when users may submit source code, documents, personal data, or credentials. If diagnostics require payload logging, use a controlled test fixture and remove or protect the logs after the investigation.
AI agents need additional care. A model endpoint that can call tools, access files, query private systems, or execute actions has a larger risk surface than a text-only completion endpoint. Authenticate both the caller and downstream tools, scope permissions narrowly, validate inputs, and avoid granting the model process unrestricted host access.
Stop the Localtonet tunnel when the remote workflow is finished. A stopped tunnel removes the public route, although it does not replace application authentication for periods when the route is active. Delete obsolete tunnels and rotate any credentials that might have been exposed during testing.
Troubleshooting matrix by symptom
| Symptom | Likely layer | What to verify next |
|---|---|---|
| Model never becomes ready | Model loading or memory | Runtime logs, allocation errors, process exits, available memory, supported model format |
| Short prompts work but long prompts fail | Context capacity or prompt processing | Actual payload size, conversation history, tool schemas, context settings, memory growth |
| First request is much slower | Cold initialization | Model load state and repeated warm requests with the same fixture |
| First token takes a long time | Queue, prompt processing, or cold start | Prompt size, queue depth, warm state, concurrent traffic |
| First token is fast but output crawls | Generation performance | Tokens per second, host utilization, model size, runtime settings, contention |
| Second user waits behind the first | Server concurrency policy | Documented worker, queue, batching, and concurrency behavior |
| Both concurrent requests become slow | Shared resource contention | Aggregate memory, accelerator utilization, bandwidth pressure, output lengths |
| Local stream is incremental, remote UI is not | Client or response buffering | Public request with a simple streaming-capable client and chunk timestamps |
| Local request is refused | Local listener | Process state, configured bind address, target port, host firewall rules |
| Local works but public URL does not connect | Tunnel lifecycle or target reachability | Client connection, tunnel running state, selected device, local IP and port |
| Public URL connects but API returns an error | Application request | HTTP status, response body, API path, headers, authentication, request schema |
| Only one remote network is slow | Remote network path | Wireless quality, mobile-network variation, packet loss, client load, another connection |
A disciplined order of operations
Start with correctness, then capacity, then performance. Confirm that the API responds locally. Confirm that the model remains loaded without memory failure. Establish cold and warm timings. Separate prompt processing from generation. Test streaming. Add concurrency. Finally compare the same request through the Localtonet public endpoint.
Preserve test results as a small timeline rather than an unstructured collection of impressions. Include configuration identifiers such as model name and runtime version when safe, but never include secrets. After changing one variable, repeat the same baseline. This makes regressions visible and prevents a temporary warm-cache improvement from being mistaken for a permanent fix.
Once the bottleneck is identified, choose a remedy that acts on that layer. Reduce model or context demand for capacity problems. Tune only documented runtime settings for inference problems. Add queue discipline for contention. Correct streaming clients or intermediaries for buffering. Repair the Localtonet target or lifecycle state for reachability. Improve or change the remote network path only when controlled measurements show that transport is the significant difference.
Frequently asked questions
Can a Localtonet HTTP tunnel make my local LLM generate faster?
No. The model still runs on your local hardware through your selected inference runtime. The tunnel provides a public route to that existing HTTP service. It does not add model memory or inference compute. Measure the local API first so you know the performance available before network transport is added.
Why does my model load successfully but fail with long prompts?
Loading the weights does not represent the complete memory requirement. Long context, runtime overhead, and concurrent sessions can require additional memory and computation. Compare short and long payloads, inspect the complete submitted context, and observe memory while the request is running. Use only context controls documented by your model runtime.
Why is the first request slower than later requests?
The first request may trigger model loading, memory allocation, accelerator initialization, or other runtime setup. Label cold and warm tests separately. Restart the runtime and repeat the sequence if you need to confirm that initialization is responsible.
Does streaming increase tokens per second?
Streaming normally changes when the client sees generated content, not how quickly the model performs the inference work. It can reduce perceived waiting by displaying partial output. Measure generation rate independently from time to first token and total completion time.
Why does my local API work while the Localtonet URL does not?
Confirm that the correct Localtonet client device is connected, the HTTP tunnel is started, and its local IP and port match the verified API listener. If the client runs on another device, that device must be able to reach the target. Creating the tunnel alone does not start it.
How can I tell whether a delay comes from Localtonet or the model?
Compare the same request locally and through the public URL under equivalent conditions. Use the same warm model, prompt, output limit, streaming mode, and client. Compare first-token delay, generation rate, total time, and chunk arrival patterns. A slow local baseline identifies an inference-side problem. A consistent difference that appears only through the public path justifies investigating transport, tunnel state, and remote-client behavior.
Should I expose Ollama or another model API directly without authentication?
A public URL should not be treated like a private loopback endpoint. Require suitable authentication and authorization, restrict the exposed operations, protect credentials, and avoid exposing administrative or tool-execution routes unnecessarily. The precise controls depend on the model server or gateway you use. Stop the tunnel when remote access is no longer required.
Test your local LLM remotely with Localtonet
Establish a reliable local inference baseline, create an HTTP tunnel to the verified local API, and compare equivalent requests without router port forwarding or a public IP address. Keep access authenticated and stop the tunnel when your testing session is complete.
Get Started Free โ