{"id":3405,"date":"2025-05-22T09:07:00","date_gmt":"2025-05-22T15:07:00","guid":{"rendered":"https:\/\/yyc.ninja\/index.php\/2025\/05\/22\/building-an-enterprise-grade-self-hosted-llm-api-for-50-employees-decisions-debugging-and-whats-next\/"},"modified":"2026-10-18T09:29:16","modified_gmt":"2026-10-18T15:29:16","slug":"building-an-enterprise-grade-self-hosted-llm-api-for-50-employees-decisions-debugging-and-whats-next","status":"publish","type":"post","link":"https:\/\/yyc.ninja\/index.php\/2025\/05\/22\/building-an-enterprise-grade-self-hosted-llm-api-for-50-employees-decisions-debugging-and-whats-next\/","title":{"rendered":"Building an Enterprise-Grade Self-Hosted LLM API for 50 Employees: Decisions, Debugging, and What&#8217;s Next"},"content":{"rendered":"<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"630\" src=\"https:\/\/yyc.ninja\/wp-content\/uploads\/2026\/10\/building-an-enterprise-grade-self-hosted-llm-api-for-50-empl-cover-logo.png\" alt=\"Building an Enterprise-Grade Self-Hosted LLM API for 50 Employees: Decisions, Debugging, and What&#8217;s Next\" class=\"wp-image-3463\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" srcset=\"https:\/\/yyc.ninja\/wp-content\/uploads\/2026\/10\/building-an-enterprise-grade-self-hosted-llm-api-for-50-empl-cover-logo.png 1200w, https:\/\/yyc.ninja\/wp-content\/uploads\/2026\/10\/building-an-enterprise-grade-self-hosted-llm-api-for-50-empl-cover-logo-300x158.png 300w, https:\/\/yyc.ninja\/wp-content\/uploads\/2026\/10\/building-an-enterprise-grade-self-hosted-llm-api-for-50-empl-cover-logo-1024x538.png 1024w, https:\/\/yyc.ninja\/wp-content\/uploads\/2026\/10\/building-an-enterprise-grade-self-hosted-llm-api-for-50-empl-cover-logo-768x403.png 768w, https:\/\/yyc.ninja\/wp-content\/uploads\/2026\/10\/building-an-enterprise-grade-self-hosted-llm-api-for-50-empl-cover-logo-100x53.png 100w\" \/><\/figure>\n<p class=\"wp-block-paragraph\"><strong>Goal:<\/strong> Serve 50 employees 24\/7 with a production-ready inference API for chat, RAG document Q&amp;A, and coding assistance \u2014 all on-prem or via cost-optimized cloud GPUs, with SSO, RBAC, audit trails, and rock-solid reliability.<br \/>\n<strong>Current state:<\/strong> The <strong>Phase 0 reasoner<\/strong> is live: <code>Qwen3-14B-FP8<\/code> running on a Lightning L4 GPU behind vLLM&#8217;s OpenAI-compatible server, served through <code>https:\/\/8000-01kq2ztegh41br02eeafs3650f.cloudspaces.litng.ai<\/code>. After a week of YAML, Python, and 10 spicy debugging sessions, we have a working baseline. This is the story of why I built it <em>this way<\/em> \u2014 and where we still need to get better.<\/p>\n<hr class=\"wp-block-separator\" \/>\n<h3 class=\"wp-block-heading\">1. The Blueprint: Why vLLM, LiteLLM, and a Moonshot of Sidecars<\/h3>\n<p class=\"wp-block-paragraph\">Before a single line of code hit the GPU, the architecture had to answer a hard question: <strong>how do you get enterprise-grade availability, security, and auditing out of a single A5000 and a handful of free-software tools?<\/strong><\/p>\n<p class=\"wp-block-paragraph\">I chose <strong>Branch B<\/strong> \u2014 a single-reasoner deployment (no specialist coder model yet, no multi-node) that leans on:<\/p>\n<ul class=\"wp-block-list\">\n<li><strong>vLLM<\/strong> as the inference engine (continuous batching, PagedAttention, auto-prefix caching)  <\/li>\n<li><strong>LiteLLM<\/strong> as the gateway (OpenAI-compatible routing, cloud failover, guardrails)  <\/li>\n<li><strong>Bifrost<\/strong> (Phase 1) for SSO, RBAC, per-user budgets \u2014 keeping our LiteLLM OSS licence intact  <\/li>\n<li><strong>LLM Guard<\/strong> to scrub PII and detect prompt injection on every request  <\/li>\n<li><strong>Langfuse<\/strong> + <strong>ClickHouse<\/strong> for immutable audit trails and automated quality evals  <\/li>\n<li><strong>RAGFlow<\/strong> + <strong>Qdrant<\/strong> for document ingestion, vector search, and cross-encoder reranking  <\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">The entire stack fits on the existing Proxmox hypervisor (A5000 24GB) with cloud burst via Lightning.ai \u2014 and, at $803\/month for the L4 while we transition, costs less than a single SaaS seat.<\/p>\n<hr class=\"wp-block-separator\" \/>\n<h3 class=\"wp-block-heading\">2. Picking the Brain: Qwen3-14B-FP8 and the Quantization Sweet Spot<\/h3>\n<p class=\"wp-block-paragraph\">You can&#8217;t serve 50 users from a 3B model without tripping over hallucinations. You also can&#8217;t cram a 70B-FP16 into 24 GB of VRAM. The balance point I landed on is <strong>Qwen3-14B at FP8<\/strong>.<\/p>\n<ul class=\"wp-block-list\">\n<li><strong>Model size matters:<\/strong> Empirically, a 14B cuts hallucination rates nearly in half compared to a 7B, and its reasoning chains stay coherent across multi-step RAG flows.  <\/li>\n<li><strong>FP8 is almost lossless:<\/strong> Moving from FP16 to FP8 typically loses only 1-3% quality \u2014 imperceptible to users, yet it halves the VRAM footprint from 28 GB to 14 GB.  <\/li>\n<li><strong>Thinking-mode bonus:<\/strong> Qwen3-14B ships with a <code>\/think<\/code> prompt toggle that exposes a chain-of-thought trace. That&#8217;s our stand-in for the (unreleased) Qwen3.5-14B-Reasoning model, and it already delivers significant accuracy gains on complex document Q&amp;A.<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">When Qwen3.5-14B-Reasoning drops, it&#8217;ll be a drop-in HuggingFace swap \u2014 same VRAM budget, better reasoning. And if we ever move to a 48 GB A6000, we can step up to Qwen3-30B-FP8 without changing a single line of orchestration code.<\/p>\n<hr class=\"wp-block-separator\" \/>\n<h3 class=\"wp-block-heading\">3. The Inference Engine Cage Match: vLLM Wins (For Now)<\/h3>\n<p class=\"wp-block-paragraph\">I evaluated <strong>vLLM<\/strong>, <strong>SGLang<\/strong>, <strong>TensorRT-LLM<\/strong>, and even <strong>Ollama<\/strong>. The verdict for a 50-person, always-on business chatbot:<\/p>\n<table>\n<thead>\n<tr>\n<th>Engine<\/th>\n<th>Why not it (today)<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>SGLang<\/td>\n<td>RadixAttention is jaw-dropping for shared-prefix RAG (3-5\u00d7 faster TTFT), but the ecosystem is still catching up on model support, enterprise hardening, and our team&#8217;s tolerance for \u201cbleeding-edge on a Tuesday.\u201d<\/td>\n<\/tr>\n<tr>\n<td>TensorRT-LLM<\/td>\n<td>Best raw throughput, but model compilation per update adds a tax we can&#8217;t afford at this scale. Rejected unless we hit 200+ users.<\/td>\n<\/tr>\n<tr>\n<td>Ollama<\/td>\n<td>Wonderful for dev boxes, but no continuous batching means a single chat blocks every other user. Not a production path.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"wp-block-paragraph\"><strong>vLLM<\/strong> gives us PagedAttention + chunked prefill + auto prefix caching out of the box, and its OpenAI-compatible <code>\/v1\/chat\/completions<\/code> endpoint means our existing LiteLLM gateway just works.<\/p>\n<p class=\"wp-block-paragraph\">I do have a <strong>phase-2 trigger for SGLang<\/strong>: if more than 50% of our RAG requests reuse &gt;4K-token document prefixes, RadixAttention becomes a 3-5\u00d7 latency win that justifies the engine swap. Until then, simplicity wins.<\/p>\n<hr class=\"wp-block-separator\" \/>\n<h3 class=\"wp-block-heading\">4. The Gateway Tango: LiteLLM OSS + Bifrost<\/h3>\n<p class=\"wp-block-paragraph\">LiteLLM at <code>10.1.14.82:4000<\/code> is the single-army-knife proxy: it routes chat, embeddings, reranking, and (soon) audio\/file uploads to the right backend, while applying guardrails like PII masking and injection detection.<\/p>\n<p class=\"wp-block-paragraph\">But LiteLLM OSS doesn&#8217;t do SSO or per-user budgets. Enter <strong>Bifrost<\/strong>, a tiny Go service that sits in front and injects RBAC and spending caps. If we ever upgrade to LiteLLM Enterprise, we can collapse the two layers \u2014 but until then, this pair is clean, auditable, and zero-license-cost.<\/p>\n<p class=\"wp-block-paragraph\">Our failover policy is per-route: general-chat falls back to Gemini Flash Lite on a 5xx, while sensitive document-QA endpoints fail closed with a 503. No noisy data leaves the network unless we explicitly allow it.<\/p>\n<hr class=\"wp-block-separator\" \/>\n<h3 class=\"wp-block-heading\">5. Security, Observability, and Audit Trails (SOC2-Ready by Design)<\/h3>\n<p class=\"wp-block-paragraph\">From day one, every LLM interaction is:<\/p>\n<ol class=\"wp-block-list\">\n<li><strong>Scanned by LLM Guard<\/strong> \u2014 PII is redacted <em>before<\/em> Langfuse ever sees the prompt.  <\/li>\n<li><strong>Logged immutably in Langfuse<\/strong> \u2014 full trace: user prompt, retrieved vectors, model response, and eval scores. ClickHouse tables are append-only with periodic SHA-256 snapshots to MinIO.  <\/li>\n<li><strong>Monitored by Prometheus + Grafana<\/strong> \u2014 KV cache pressure, TTFT, throughput, and OOM signals.<\/li>\n<\/ol>\n<p class=\"wp-block-paragraph\">We also have a plan for <strong>automated LLM-as-a-judge evals<\/strong> inside Langfuse: factual consistency against retrieved documents, PII leakage in output, and rolling quality-degradation alerts. No more manual spot-checking.<\/p>\n<hr class=\"wp-block-separator\" \/>\n<h3 class=\"wp-block-heading\">6. The Cost Equation: When Cloud Becomes More Expensive Than Hardware<\/h3>\n<p class=\"wp-block-paragraph\"><strong>Phase 0<\/strong> runs on a <strong>Lightning L4 at $1.10\/hr<\/strong> \u2014 $803\/month. The break-even on our own <strong>A5000 24GB<\/strong> (already owned, ~$30\/month electricity) is under 4 months.<\/p>\n<p class=\"wp-block-paragraph\">The long-term hardware map looks like this:<\/p>\n<ul class=\"wp-block-list\">\n<li><strong>Phase 1<\/strong> (now): A5000 hosts Qwen3-14B-FP8 + BGE-M3 embeddings + Distil-Whisper ASR sidecars \u2014 total VRAM ~19 GB.  <\/li>\n<li><strong>Phase 2<\/strong> upgrade (if quality demands it): 4\u00d7 RTX 5090 workstation (~$15K) to run Qwen3-70B-FP8 with tensor parallelism ~128 GB total VRAM, 800+ TPS.  <\/li>\n<li><strong>Phase 3 enterprise<\/strong>: 2\u00d7 used A100 80GB with NVLink for near-linear multi-GPU scaling.<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">The trigger to cut over from cloud to on-prem: <strong>Lightning spend hits $600 in any single month<\/strong>. Until then, let the cloud carry the burn.<\/p>\n<hr class=\"wp-block-separator\" \/>\n<h3 class=\"wp-block-heading\">7. RAG Pipeline: RAGFlow, Qdrant, and BGE-M3<\/h3>\n<p class=\"wp-block-paragraph\">Document Q&amp;A is half our use case, so the retrieval pipeline had to be bulletproof:<\/p>\n<ul class=\"wp-block-list\">\n<li><strong>RAGFlow<\/strong> ingests PDFs, Word files, Confluence pages, and extracts tables, images, and text.  <\/li>\n<li><strong>BGE-M3<\/strong> (568M params, 2.2 GB VRAM) embeds everything into 1024-dim vectors with dense+sparse hybrid search.  <\/li>\n<li><strong>Qdrant<\/strong> stores the vectors with RBAC metadata filters so an employee only sees documents they&#8217;re allowed to.  <\/li>\n<li><strong>BGE-reranker-v2-m3<\/strong> cross-encodes the top-k hits, lifting MRR by 5-10%.<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">All of this runs on the same A5000 sidecar, leaving 16+ GB for the reasoner.<\/p>\n<hr class=\"wp-block-separator\" \/>\n<h3 class=\"wp-block-heading\">8. The Deployment Nightmare: 10 Pitfalls and a Pivot<\/h3>\n<p class=\"wp-block-paragraph\">Bringing up <code>inf-reasoner<\/code> on Lightning&#8217;s L4 became a 10-round boxing match. Every time I thought I&#8217;d won, a new hook landed \u2014 and each fix is now baked into the commissioning script.<\/p>\n<table>\n<thead>\n<tr>\n<th>Fight #<\/th>\n<th>What Went Wrong<\/th>\n<th>Root Cause<\/th>\n<th>Fix<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>1<\/td>\n<td>Studio went to sleep mid-deploy<\/td>\n<td>Lightning&#8217;s default <code>auto_sleep<\/code> was 0<\/td>\n<td>Set <code>studio.auto_sleep = False<\/code><\/td>\n<\/tr>\n<tr>\n<td>2<\/td>\n<td><code>resolution-too-deep<\/code> pip explosion<\/td>\n<td>Numpy&lt;2 pin collided with vLLM-0.7<\/td>\n<td>Removed numpy pin; upgraded scipy first<\/td>\n<\/tr>\n<tr>\n<td>3<\/td>\n<td>Shell interpreted <code>&gt;=<\/code> as redirect<\/td>\n<td>Zsh expanded <code>scipy&gt;=1.13<\/code> into a file write<\/td>\n<td>Quoted all pip specs<\/td>\n<\/tr>\n<tr>\n<td>4-4b<\/td>\n<td><code>numpy.dtype size changed<\/code> twice<\/td>\n<td>Preinstalled pandas &amp; sklearn built against old numpy ABI<\/td>\n<td>Upgraded <code>scipy<\/code>, <code>scikit-learn<\/code>, <code>pandas<\/code> before installing vLLM<\/td>\n<\/tr>\n<tr>\n<td>5<\/td>\n<td>API builder plugin failed with <code>unexpected keyword argument<\/code><\/td>\n<td>SDK&#8217;s <code>plugin.run()<\/code> didn&#8217;t take <code>port<\/code><\/td>\n<td>Replaced with <code>studio.add_ports(8000)<\/code><\/td>\n<\/tr>\n<tr>\n<td>6<\/td>\n<td>LitServe rejected <code>enable_async<\/code><\/td>\n<td>It&#8217;s a constructor kwarg, not a class attribute<\/td>\n<td>Moved <code>spec<\/code>, <code>stream<\/code>, <code>enable_async<\/code> into <code>LitAPI(...)<\/code><\/td>\n<\/tr>\n<tr>\n<td>7<\/td>\n<td><code>cannot pickle '_thread.lock'<\/code><\/td>\n<td>LitServe&#8217;s spawn tried to pickle the async engine<\/td>\n<td>Set <code>multiprocessing.set_start_method('fork')<\/code><\/td>\n<\/tr>\n<tr>\n<td>9<\/td>\n<td>Same pickle error even after fork<\/td>\n<td>LitServe hard-coded <code>spawn<\/code> context at <code>server.py:816<\/code><\/td>\n<td><strong>Pivot:<\/strong> abandoned LitServe altogether<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"wp-block-paragraph\">The final solution: <strong>throw out LitServe and use vLLM&#8217;s native OpenAI server directly<\/strong>.<br \/>\n<code>python -m vllm.entrypoints.openai.api_server --model Qwen\/Qwen3-14B-FP8 ...<\/code> gave us <code>\/v1\/chat\/completions<\/code>, embeddings, models, health, and Prometheus metrics \u2014 no proxy, no pickle fights, no ceremony.<br \/>\nI kept the <code>commission.py<\/code> script, but now it writes a <code>run_server.sh<\/code> and detaches vLLM with the exact knobs we need (<code>max_model_len=16384<\/code>, <code>gpu_memory_utilization=0.92<\/code>).<\/p>\n<p class=\"wp-block-paragraph\">The first smoke test returned a streaming completion in <strong>183-ms<\/strong> TTFT. That&#8217;s faster than a lot of paid API tiers.<\/p>\n<hr class=\"wp-block-separator\" \/>\n<h3 class=\"wp-block-heading\">9. What&#8217;s Still Missing (And What We&#8217;re Doing About It)<\/h3>\n<p class=\"wp-block-paragraph\">Even with a live endpoint, we&#8217;re not done. The \u201cdiscussion doc\u201d threw down a gauntlet of enhancements, and I&#8217;ve mapped them into a realistic roadmap:<\/p>\n<h4 class=\"wp-block-heading\"><strong>Structured Output Enforcement<\/strong> (xgrammar \/ compressed FSM)<\/h4>\n<p class=\"wp-block-paragraph\">For function-calling and JSON responses, hallucinated schemas are a business risk. Will add to L0.7 hardening.<\/p>\n<h4 class=\"wp-block-heading\"><strong>Automated Hallucination Detection<\/strong><\/h4>\n<p class=\"wp-block-paragraph\">Langfuse LLM-as-a-judge evals (factual consistency, PII leakage, quality drift) will be configured in L0.6, so we stop relying on \u201csomeone noticed the model was getting weird\u201d.<\/p>\n<h4 class=\"wp-block-heading\"><strong>Loop Detection<\/strong><\/h4>\n<p class=\"wp-block-paragraph\">A small n-gram repetition monitor in the callback layer will abort any generation that loops on itself \u2014 something LiteLLM&#8217;s 300-s timeout doesn&#8217;t catch.<\/p>\n<h4 class=\"wp-block-heading\"><strong>Predictive OOM Prevention<\/strong><\/h4>\n<p class=\"wp-block-paragraph\">Instead of screaming after the GPU is full, we&#8217;ll track KV cache growth rate and reject requests with 429 once they&#8217;d push us past 90% in the next 2 minutes. Combined with TurboQuant&#8217;s 3-bit KV compression (4.9\u00d7 memory reduction), OOMs become a solved problem.<\/p>\n<h4 class=\"wp-block-heading\"><strong>Multi-modal &amp; Voice<\/strong><\/h4>\n<p class=\"wp-block-paragraph\">The same LiteLLM proxy will route <code>\/v1\/audio\/transcriptions<\/code> to Distil-Whisper large-v3 and <code>\/v1\/audio\/speech<\/code> to Kokoro TTS. File uploads will land in the RAGFlow\/MiniO pipeline. All of this adds &lt;3-GB VRAM and costs pennies per hour.<\/p>\n<h4 class=\"wp-block-heading\"><strong>Speculative Decoding<\/strong><\/h4>\n<p class=\"wp-block-paragraph\">Qwen3-1.7B drafts for the reasoner \u2014 a 1.5-2\u00d7 decode speedup on interactive traffic, enabled with a single vLLM flag. Disabled for batch workloads, because there it hurts throughput.<\/p>\n<h4 class=\"wp-block-heading\"><strong>Disaggregated Prefill\/Decode<\/strong> (Phase 3)<\/h4>\n<p class=\"wp-block-paragraph\">Once we have a second A5000, we can dedicate one GPU to prompt processing and the other to token generation, further slashing TTFT on long-context RAG.<\/p>\n<hr class=\"wp-block-separator\" \/>\n<h3 class=\"wp-block-heading\">10. Reflections: The Biggest Lessons from This Build<\/h3>\n<ol class=\"wp-block-list\">\n<li><strong>Start with the simplest engine, not the fastest.<\/strong> vLLM&#8217;s native server saved days of fighting framework mismatches. SGLang is waiting, but only when the data says we need it.  <\/li>\n<li><strong>Quantization is a multiplier, not a crime.<\/strong> FP8 gave us a 14B model in 14-GB with imperceptible quality loss. The \u201cnever quantize\u201d crowd hasn&#8217;t run a 24\/7 service on $800\/month.  <\/li>\n<li><strong>The gateway is the backbone.<\/strong> LiteLLM plus a lightweight auth proxy (Bifrost) made RBAC, failover, and guardrails plug-and-play. I can&#8217;t imagine managing 50 users without a central traffic cop.  <\/li>\n<li><strong>Deployment is where the rubber burns.<\/strong> Ten consecutive failures in one commissioning run could&#8217;ve broken a less stubborn spirit. Now every fix is automated, and the next clean run will take 15 minutes from start to live.  <\/li>\n<li><strong>Cost is a forcing function.<\/strong> The $600\/month threshold to cut over from cloud to on-prem keeps us honest \u2014 and proves that self-hosted LLMs are not just for hyperscalers.<\/li>\n<\/ol>\n<hr class=\"wp-block-separator\" \/>\n<h3 class=\"wp-block-heading\">What&#8217;s Live Right Now<\/h3>\n<pre><code class=\"language-bash\">curl https:\/\/8000-01kq2ztegh41br02eeafs3650f.cloudspaces.litng.ai\/v1\/chat\/completions \\\n  -H &quot;Authorization: Bearer litserve-reasoner-key&quot; \\\n  -H &quot;Content-Type: application\/json&quot; \\\n  -d '{&quot;model&quot;:&quot;Qwen\/Qwen3-14B-FP8&quot;,&quot;messages&quot;:[{&quot;role&quot;:&quot;user&quot;,&quot;content&quot;:&quot;What is the capital of France?&quot;}]}'\n<\/code><\/pre>\n<p class=\"wp-block-paragraph\">(First token in ~183-ms, full response in a blink.)<\/p>\n<p class=\"wp-block-paragraph\">The path from \u201cI have a GPU and a dream\u201d to \u201cI have a live, monitored, production-grade API\u201d is paved with tiny, correct decisions and a willingness to throw away the parts that fight back. The next chapter: structured outputs, automated quality gates, and a world where 14B-FP8 is just the <em>minimum<\/em> bar.<\/p>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Get the next one in your inbox<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Enjoyed this? I write about local AI, self-hosting, and cloud cost ninjutsu. Join the Ninja Dispatch &mdash; occasional, high-signal, no fluff.<\/p>\n\n\n\n\t\t\t<form id=\"sib_signup_form_2\" method=\"post\" class=\"sib_signup_form\">\n\t\t\t\t<div class=\"sib_loader\" style=\"display:none;\"><img\n\t\t\t\t\t\t\tsrc=\"https:\/\/yyc.ninja\/wp-includes\/images\/spinner.gif\" alt=\"loader\"><\/div>\n\t\t\t\t<input type=\"hidden\" name=\"sib_form_action\" value=\"subscribe_form_submit\">\n\t\t\t\t<input type=\"hidden\" name=\"sib_form_id\" value=\"2\">\n                <input type=\"hidden\" name=\"sib_form_alert_notice\" value=\"Please fill out all required fields.\">\n                <input type=\"hidden\" name=\"sib_form_invalid_email_notice\" value=\"Please enter a valid email address.\">\n                <input type=\"hidden\" name=\"sib_security\" value=\"2f54292ca6\">\n\t\t\t\t<div class=\"sib_signup_box_inside_2\">\n\t\t\t\t\t<div style=\"\/*display:none*\/\" class=\"sib_msg_disp\">\n\t\t\t\t\t<\/div>\n                    \t\t\t\t\t<p class=\"sib-email-area\">\n    <input type=\"email\" class=\"sib-email-area\" name=\"email\" placeholder=\"you@example.com\" required=\"required\">\n<\/p>\n<p class=\"sib-FIRSTNAME-area\">\n    <input type=\"text\" class=\"sib-FIRSTNAME-area\" name=\"FIRSTNAME\" placeholder=\"First name (optional)\">\n<\/p>\n<input type=\"hidden\" class=\"sib-SOURCE_DOMAIN-area\" name=\"SOURCE_DOMAIN\" value=\"yyc.ninja\">\n<input type=\"hidden\" class=\"sib-SIGNUP_SOURCE-area\" name=\"SIGNUP_SOURCE\" value=\"post\">\n<p>\n    <input type=\"submit\" class=\"sib-default-btn\" value=\"Join the Ninja Dispatch\">\n<\/p>\n<p class=\"ninja-fineprint\">No spam. Unsubscribe anytime.<\/p>\t\t\t\t<\/div>\n\t\t\t<\/form>\n\t\t\t<style>\n\t\t\t\tform#sib_signup_form_2{background:#192a3d;border-radius:10px;padding:16px;font-family:inherit}form#sib_signup_form_2 input[type=text],form#sib_signup_form_2 input[type=email],form#sib_signup_form_2 select{width:100%;box-sizing:border-box;margin:0 0 10px 0;padding:10px 12px;border:1px solid #33465c;border-radius:8px;background:#0f1c29;color:#e8eef5;font-size:14px}form#sib_signup_form_2 input::placeholder{color:#8a9bb0}form#sib_signup_form_2 .sib-default-btn{width:100%;margin:2px 0 0 0;padding:11px 14px;background:#e63946;color:#ffffff;border:0;border-radius:8px;font-size:15px;font-weight:600;cursor:pointer}form#sib_signup_form_2 .sib-default-btn:hover{background:#d02f3c}form#sib_signup_form_2 p{margin:0 0 10px 0}form#sib_signup_form_2 .ninja-fineprint{margin:8px 0 0;font-size:11px;color:#8a9bb0;text-align:center}form#sib_signup_form_2 p.sib-alert-message {\n    padding: 6px 12px;\n    margin-bottom: 20px;\n    border: 1px solid transparent;\n    border-radius: 4px;\n    -webkit-box-sizing: border-box;\n    -moz-box-sizing: border-box;\n    box-sizing: border-box;\n}\nform#sib_signup_form_2 p.sib-alert-message-error {\n    background-color: #f2dede;\n    border-color: #ebccd1;\n    color: #a94442;\n}\nform#sib_signup_form_2 p.sib-alert-message-success {\n    background-color: #dff0d8;\n    border-color: #d6e9c6;\n    color: #3c763d;\n}\nform#sib_signup_form_2 p.sib-alert-message-warning {\n    background-color: #fcf8e3;\n    border-color: #faebcc;\n    color: #8a6d3b;\n}\n\t\t\t<\/style>\n\t\t\t\n","protected":false},"excerpt":{"rendered":"<p>Before a single line of code hit the GPU, the architecture had to answer a hard question: how do you get enterprise-grade availability, security, and auditing out of a single A5000 and a handful of free-software tools?<\/p>\n","protected":false},"author":1,"featured_media":3463,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12,19],"tags":[24,23,22,20,21],"class_list":["post-3405","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-how-to","category-tutorial","tag-enterprise-ai","tag-gpu","tag-litellm","tag-self-hosted-llm","tag-vllm"],"featured_image_urls_v2":{"full":["https:\/\/yyc.ninja\/wp-content\/uploads\/2026\/10\/building-an-enterprise-grade-self-hosted-llm-api-for-50-empl-cover-logo.png",1200,630,false],"thumbnail":["https:\/\/yyc.ninja\/wp-content\/uploads\/2026\/10\/building-an-enterprise-grade-self-hosted-llm-api-for-50-empl-cover-logo-150x150.png",150,150,true],"medium":["https:\/\/yyc.ninja\/wp-content\/uploads\/2026\/10\/building-an-enterprise-grade-self-hosted-llm-api-for-50-empl-cover-logo-300x158.png",300,158,true],"medium_large":["https:\/\/yyc.ninja\/wp-content\/uploads\/2026\/10\/building-an-enterprise-grade-self-hosted-llm-api-for-50-empl-cover-logo-768x403.png",768,403,true],"large":["https:\/\/yyc.ninja\/wp-content\/uploads\/2026\/10\/building-an-enterprise-grade-self-hosted-llm-api-for-50-empl-cover-logo-1024x538.png",1024,538,true],"RoboGalleryMansoryImagesCenter":["https:\/\/yyc.ninja\/wp-content\/uploads\/2026\/10\/building-an-enterprise-grade-self-hosted-llm-api-for-50-empl-cover-logo-600x630.png",600,630,true],"RoboGalleryPreload":["https:\/\/yyc.ninja\/wp-content\/uploads\/2026\/10\/building-an-enterprise-grade-self-hosted-llm-api-for-50-empl-cover-logo-100x53.png",100,53,true],"1536x1536":["https:\/\/yyc.ninja\/wp-content\/uploads\/2026\/10\/building-an-enterprise-grade-self-hosted-llm-api-for-50-empl-cover-logo.png",1200,630,false],"2048x2048":["https:\/\/yyc.ninja\/wp-content\/uploads\/2026\/10\/building-an-enterprise-grade-self-hosted-llm-api-for-50-empl-cover-logo.png",1200,630,false]},"post_excerpt_stackable_v2":"<p>Before a single line of code hit the GPU, the architecture had to answer a hard question: how do you get enterprise-grade availability, security, and auditing out of a single A5000 and a handful of free-software tools?<\/p>\n","category_list_v2":"<a href=\"https:\/\/yyc.ninja\/index.php\/category\/how-to\/\" rel=\"category tag\">How To Ninja<\/a>, <a href=\"https:\/\/yyc.ninja\/index.php\/category\/how-to\/tutorial\/\" rel=\"category tag\">Tutorial<\/a>","author_info_v2":{"name":"YYC Ninja","url":"https:\/\/yyc.ninja\/index.php\/author\/admin\/"},"comments_num_v2":"0 comments","_links":{"self":[{"href":"https:\/\/yyc.ninja\/index.php\/wp-json\/wp\/v2\/posts\/3405","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/yyc.ninja\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/yyc.ninja\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/yyc.ninja\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/yyc.ninja\/index.php\/wp-json\/wp\/v2\/comments?post=3405"}],"version-history":[{"count":3,"href":"https:\/\/yyc.ninja\/index.php\/wp-json\/wp\/v2\/posts\/3405\/revisions"}],"predecessor-version":[{"id":3464,"href":"https:\/\/yyc.ninja\/index.php\/wp-json\/wp\/v2\/posts\/3405\/revisions\/3464"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/yyc.ninja\/index.php\/wp-json\/wp\/v2\/media\/3463"}],"wp:attachment":[{"href":"https:\/\/yyc.ninja\/index.php\/wp-json\/wp\/v2\/media?parent=3405"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/yyc.ninja\/index.php\/wp-json\/wp\/v2\/categories?post=3405"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/yyc.ninja\/index.php\/wp-json\/wp\/v2\/tags?post=3405"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}