Skip to content

Commit f259180

Browse files
dskarzhashvayka
authored andcommitted
Move Ollama with Nginx reverse proxy guide to a separate page
1 parent d26ac30 commit f259180

File tree

3 files changed

+373
-0
lines changed

3 files changed

+373
-0
lines changed

_data/pages_info.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4086,6 +4086,9 @@
40864086
"/docs/samples/analytics/mcp-server-ai-insights/":
40874087
url: "/docs/samples/analytics/mcp-server-ai-insights/"
40884088
redirect_from: []
4089+
"/docs/samples/analytics/ollama/nginx/":
4090+
url: "/docs/samples/analytics/ollama/nginx/"
4091+
redirect_from: []
40894092
"/docs/samples/analytics/ollama/":
40904093
url: "/docs/samples/analytics/ollama/"
40914094
redirect_from: []
Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
* TOC
2+
{:toc}
3+
4+
## Overview {#overview}
5+
6+
[Ollama](https://ollama.com/){:target="_blank"} is a powerful tool for running Large Language Models (LLMs) locally, but it does not include built-in authentication mechanisms.
7+
When exposing Ollama on a network, securing the API endpoint becomes your responsibility.
8+
9+
This guide demonstrates how to deploy Ollama with [Nginx](https://nginx.org/){:target="_blank"} as a [reverse proxy](https://en.wikipedia.org/wiki/Reverse_proxy){:target="_blank"}
10+
to add authentication to your Ollama deployment. The Nginx proxy acts as a security gatekeeper, validating credentials before forwarding requests to the Ollama service.
11+
12+
We will cover two common authentication methods:
13+
- **HTTP Basic Authentication** (username and password)
14+
- **Bearer Token Authentication** (a secret API key)
15+
16+
Both services, Ollama and Nginx, will be deployed together as containers using Docker Compose.
17+
This guide focuses on demonstrating the concept with a working implementation that you can use as a foundation for further customization.
18+
We use the standard Ollama Docker image without GPU acceleration to keep the setup straightforward, though GPU support can be added later for improved performance.
19+
20+
{% capture https_warning %}
21+
After completing this guide, we **strongly recommend** securing your [Nginx proxy with HTTPS](https://nginx.org/en/docs/http/configuring_https_servers.html){:target="_blank"}
22+
to ensure that credentials (passwords or bearer tokens) are always encrypted and not sent in plain text over the network.
23+
{% endcapture %}
24+
{% include templates/warn-banner.md content=https_warning %}
25+
26+
## Prerequisites {#prerequisites}
27+
28+
Before you start, ensure you have Docker and Docker Compose installed.
29+
The easiest way to get both is to install [Docker Desktop](https://docs.docker.com/desktop/){:target="_blank"} and ensure it is running before you proceed.
30+
31+
## Setup: Project Directory {#project-setup}
32+
33+
First, create a main project directory named `ollama-nginx-auth`. All the files we create throughout this guide will be placed inside this directory.
34+
35+
Next, inside the `ollama-nginx-auth` directory, create another directory named `nginx`. This is where you will store your Nginx-specific configuration files.
36+
37+
After you are done, your directory structure should look like this:
38+
```
39+
ollama-nginx-auth/
40+
└── nginx/
41+
```
42+
43+
Make sure you are working inside the main `ollama-nginx-auth` directory for the next steps.
44+
45+
## Approach 1: HTTP Basic Authentication {#basic-auth}
46+
47+
This method protects your endpoint with a simple username and password.
48+
When a request is made, Nginx checks the provided credentials against an encrypted list of users in a `.htpasswd` file to grant or deny access.
49+
50+
The `.htpasswd` file is a standard file used for storing usernames and passwords for basic authentication on web servers like Nginx.
51+
Each line in the file represents a single user and contains the username followed by a colon and the encrypted (hashed) password.
52+
53+
### Step 1: Create the Credential File {#basic-credentials}
54+
55+
From your project root (`ollama-nginx-auth`), create the `.htpasswd` file inside the `nginx` directory. This command creates a file with the username `myuser` and password `mypassword`.
56+
57+
{% capture tabspec %}htpasswd-setup
58+
htpasswd-setup-linux-macos,Linux/macOS,shell,/docs/samples/analytics/resources/htpasswd-setup-linux-macos.sh
59+
htpasswd-setup-windows,Windows (PowerShell),text,/docs/samples/analytics/resources/htpasswd-setup-windows.ps1{% endcapture %}
60+
{% include tabs.html %}
61+
62+
### Step 2: Create the Nginx Configuration File {#basic-config}
63+
64+
Create a file named `basic_auth.conf` inside the `nginx` directory (`ollama-nginx-auth/nginx/basic_auth.conf`) and paste the following content into it.
65+
```
66+
events {}
67+
68+
http {
69+
server {
70+
listen 80;
71+
72+
location / {
73+
# This section enforces HTTP Basic Authentication
74+
auth_basic "Restricted Access";
75+
auth_basic_user_file /etc/nginx/.htpasswd; # Path to credentials file inside the container
76+
77+
# If authentication is successful, forward the request to Ollama
78+
proxy_pass http://ollama:11434;
79+
proxy_set_header Host $host;
80+
proxy_set_header X-Real-IP $remote_addr;
81+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
82+
83+
# Increase timeouts for slow model responses to prevent 504 Gateway Timeout errors
84+
proxy_connect_timeout 300s;
85+
proxy_send_timeout 300s;
86+
proxy_read_timeout 300s;
87+
}
88+
}
89+
}
90+
```
91+
{: .copy-code}
92+
93+
Here's what the configuration does:
94+
- `listen 80;`: Nginx listens on port 80 inside the Docker container.
95+
- `auth_basic "Restricted Access";`: Enables HTTP Basic Authentication.
96+
- `auth_basic_user_file /etc/nginx/.htpasswd;`: Specifies the location of the password file inside the container. We will mount our local file to this path.
97+
- `proxy_pass http://ollama:11434;`: Forwards any authenticated requests to the `ollama` service at its internal address.
98+
99+
### Step 3: Create the Docker Compose File {#basic-compose}
100+
101+
Create a file named `docker-compose.basic.yml` in the root of your project (`ollama-nginx-auth/docker-compose.basic.yml`) and paste the following content into it.
102+
```yml
103+
services:
104+
ollama:
105+
image: ollama/ollama
106+
container_name: ollama
107+
volumes:
108+
- ollama_data:/root/.ollama
109+
restart: unless-stopped
110+
111+
nginx:
112+
image: nginx:latest
113+
container_name: nginx_proxy
114+
ports:
115+
- "8880:80"
116+
volumes:
117+
- ./nginx/basic_auth.conf:/etc/nginx/nginx.conf:ro
118+
- ./nginx/.htpasswd:/etc/nginx/.htpasswd:ro
119+
depends_on:
120+
- ollama
121+
restart: unless-stopped
122+
123+
volumes:
124+
ollama_data:
125+
```
126+
{: .copy-code}
127+
128+
### Step 4: Run and Test {#basic-test}
129+
130+
Start the services using the dedicated compose file. The `-f` flag specifies which file to use. This may take a some time.
131+
```shell
132+
docker compose -f docker-compose.basic.yml up -d
133+
```
134+
{: .copy-code}
135+
136+
Pull a model by executing the command directly inside the Ollama container. We'll use `gemma3:1b`, a lightweight model suitable for testing. This may take a some time.
137+
```shell
138+
docker exec -it ollama ollama pull gemma3:1b
139+
```
140+
{: .copy-code}
141+
142+
Test with your user (`myuser`):
143+
144+
{% capture tabspec %}http-basic-test
145+
http-basic-test-linux-macos,Linux/macOS,shell,/docs/samples/analytics/resources/http-basic-test-linux-macos.sh
146+
http-basic-test-windows,Windows (PowerShell),text,/docs/samples/analytics/resources/http-basic-test-windows.ps1{% endcapture %}
147+
{% include tabs.html %}
148+
149+
Test an API call with incorrect credentials to see it fail:
150+
151+
{% capture tabspec %}http-basic-failed-test
152+
http-basic-failed-test-linux-macos,Linux/macOS,shell,/docs/samples/analytics/resources/http-basic-failed-test-linux-macos.sh
153+
http-basic-failed-test-windows,Windows (PowerShell),text,/docs/samples/analytics/resources/http-basic-failed-test-windows.ps1{% endcapture %}
154+
{% include tabs.html %}
155+
156+
The output will show `401 Unauthorized` error.
157+
158+
### Step 5 (Optional): Manage Users {#basic-manage-users}
159+
160+
You can easily add or remove users from the `.htpasswd` file. Changes to this file take effect immediately without needing to restart Nginx.
161+
162+
{% capture adding-users-via-htpasswd %}
163+
Always use the `htpasswd` command to add users. This utility correctly encrypts the password and ensures the credentials are stored in the format that Nginx requires.
164+
Manually adding plain-text passwords to the file will not work.
165+
{% endcapture %}
166+
{% include templates/info-banner.md content=adding-users-via-htpasswd %}
167+
168+
**To add a new user:**
169+
170+
Run the `htpasswd` command again. This example adds `anotheruser` with password `anotherpassword`.
171+
172+
{% capture tabspec %}http-basic-add-user
173+
http-basic-add-user-linux-macos,Linux/macOS,shell,/docs/samples/analytics/resources/http-basic-add-user-linux-macos.sh
174+
http-basic-add-user-windows,Windows (PowerShell),text,/docs/samples/analytics/resources/http-basic-add-user-windows.ps1{% endcapture %}
175+
{% include tabs.html %}
176+
177+
You can repeat this command for as many users as you need.
178+
179+
**To remove a user:**
180+
181+
Simply open the file `./nginx/.htpasswd` in a text editor and delete the line corresponding to the user you want to remove.
182+
183+
## Approach 2: Bearer Token (API Key) Authentication {#bearer-token}
184+
185+
This method uses a secret token. You will manage your keys in a simple text file, and Nginx will be configured to read them without needing a service restart.
186+
187+
### Step 1: Create the API Keys File {#bearer-keys}
188+
189+
Create a file named `api_keys.txt` inside the `nginx` directory (`ollama-nginx-auth/nginx/api_keys.txt`) and paste your API keys into it, one per line.
190+
```
191+
my-secret-api-key-1
192+
admin-key-abcdef
193+
```
194+
{: .copy-code}
195+
196+
### Step 2: Create the Nginx Configuration File {#bearer-config}
197+
198+
Create a file named `bearer_token.conf` inside the `nginx` directory (`ollama-nginx-auth/nginx/bearer_token.conf`) and paste the following content into it.
199+
This configuration includes a [Lua](https://www.lua.org/) script to read the API keys file dynamically.
200+
```
201+
events {}
202+
203+
http {
204+
server {
205+
listen 80;
206+
207+
location / {
208+
# Lua script to read keys from a file and check against the Authorization header
209+
# This code runs for every request to this location.
210+
access_by_lua_block {
211+
local function trim(s)
212+
return (s:gsub("^%s*(.-)%s*$", "%1"))
213+
end
214+
215+
-- Function to read keys from the file into a set for quick lookups
216+
local function get_keys_from_file(path)
217+
local keys = {}
218+
local file = io.open(path, "r")
219+
if not file then
220+
ngx.log(ngx.ERR, "cannot open api keys file: ", path)
221+
return keys
222+
end
223+
for line in file:lines() do
224+
line = trim(line)
225+
if line ~= "" then
226+
keys[line] = true
227+
end
228+
end
229+
file:close()
230+
return keys
231+
end
232+
233+
-- Path to the keys file inside the container
234+
local api_keys_file = "/etc/nginx/api_keys.txt"
235+
local valid_keys = get_keys_from_file(api_keys_file)
236+
237+
-- Check the Authorization header
238+
local auth_header = ngx.var.http_authorization or ""
239+
local _, _, token = string.find(auth_header, "Bearer%s+(.+)")
240+
241+
if not token or not valid_keys[token] then
242+
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
243+
end
244+
}
245+
246+
# If access is granted, forward the request to Ollama
247+
proxy_pass http://ollama:11434;
248+
proxy_set_header Host $host;
249+
proxy_set_header X-Real-IP $remote_addr;
250+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
251+
252+
# Increase timeouts for slow model responses to prevent 504 Gateway Timeout errors
253+
proxy_connect_timeout 300s;
254+
proxy_send_timeout 300s;
255+
proxy_read_timeout 300s;
256+
}
257+
}
258+
}
259+
```
260+
{: .copy-code}
261+
262+
Here's what the configuration does:
263+
- `listen 80;`: Nginx listens on port 80 inside the Docker container.
264+
- `access_by_lua_block`: Executes a Lua script for each request to validate the Bearer token.
265+
- The script reads valid API keys from `/etc/nginx/api_keys.txt` on every request.
266+
- It extracts the token from the `Authorization: Bearer <token>` header.
267+
- If the token is missing or not found in the valid keys list, it returns a 401 Unauthorized response.
268+
- `proxy_pass http://ollama:11434;`: Forwards any authenticated requests to the `ollama` service at its internal address.
269+
270+
### Step 3: Create the Docker Compose File {#bearer-compose}
271+
272+
Create a file named `docker-compose.bearer.yml` in the root of your project (`ollama-nginx-auth/docker-compose.bearer.yml`) and paste the following content into it.
273+
This `docker-compose.bearer.yml` uses an Nginx image that includes the required Lua module (`openresty/openresty`).
274+
```yml
275+
services:
276+
ollama:
277+
image: ollama/ollama
278+
container_name: ollama
279+
volumes:
280+
- ollama_data:/root/.ollama
281+
restart: unless-stopped
282+
283+
nginx:
284+
# Use the OpenResty image which includes the Nginx Lua module
285+
image: openresty/openresty:latest
286+
container_name: nginx_proxy
287+
ports:
288+
- "8880:80"
289+
volumes:
290+
# Mount the new Nginx config and the API keys file
291+
- ./nginx/bearer_token.conf:/usr/local/openresty/nginx/conf/nginx.conf:ro
292+
- ./nginx/api_keys.txt:/etc/nginx/api_keys.txt:ro
293+
depends_on:
294+
- ollama
295+
restart: unless-stopped
296+
297+
volumes:
298+
ollama_data:
299+
```
300+
{: .copy-code}
301+
302+
### Step 4: Run and Test {#bearer-test}
303+
304+
Start the services using the dedicated compose file. The `-f` flag specifies which file to use.
305+
```shell
306+
docker compose -f docker-compose.bearer.yml up -d
307+
```
308+
{: .copy-code}
309+
310+
Pull a model (this will be quick if you did it in Approach 1):
311+
```shell
312+
docker exec -it ollama ollama pull gemma3:1b
313+
```
314+
{: .copy-code}
315+
316+
Test a request using a valid API key:
317+
318+
{% capture tabspec %}bearer-test
319+
bearer-test-linux-macos,Linux/macOS,shell,/docs/samples/analytics/resources/bearer-test-linux-macos.sh
320+
bearer-test-windows,Windows (PowerShell),text,/docs/samples/analytics/resources/bearer-test-windows.ps1{% endcapture %}
321+
{% include tabs.html %}
322+
323+
Test with an invalid API key to see it fail:
324+
325+
{% capture tabspec %}bearer-failed-test
326+
bearer-failed-test-linux-macos,Linux/macOS,shell,/docs/samples/analytics/resources/bearer-failed-test-linux-macos.sh
327+
bearer-failed-test-windows,Windows (PowerShell),text,/docs/samples/analytics/resources/bearer-failed-test-windows.ps1{% endcapture %}
328+
{% include tabs.html %}
329+
330+
### Step 5 (Optional): Manage API Keys {#bearer-manage-keys}
331+
332+
Simply open the file `./nginx/api_keys.txt` in a text editor. Add, change, or remove keys (one per line). Save the file.
333+
334+
The changes take effect immediately on the next API request because the Lua script reads the file every time a request is made.
335+
336+
For example, you can edit the file, remove the `admin-key-abcdef` key, save it, and then try to use that key in a test request.
337+
The request will now fail with a 401 Unauthorized error.
338+
339+
## Usage {#usage}
340+
341+
To start or stop the services, you will use the `docker compose up` and `docker compose down` commands,
342+
making sure to specify the appropriate file for the authentication approach you want to use (`docker-compose.basic.yml` or `docker-compose.bearer.yml`).
343+
- To start the services for either approach, run the following command from your project directory, replacing `<compose-file-name>` with the correct file name:
344+
```shell
345+
docker compose -f <compose-file-name> up -d
346+
```
347+
{: .copy-code}
348+
349+
- When you're finished, stop the containers with the corresponding file name:
350+
```shell
351+
docker compose -f <compose-file-name> down
352+
```
353+
{: .copy-code}
354+
355+
## Next steps {#next-steps}
356+
357+
Now that you have Ollama endpoint, here are some recommended next steps:
358+
359+
- **Enable HTTPS**: Secure your Nginx proxy with HTTPS by following the [official Nginx HTTPS configuration guide](https://nginx.org/en/docs/http/configuring_https_servers.html){:target="_blank"}.
360+
361+
- **Add GPU Support**: Enable GPU acceleration for Ollama to significantly improve inference speed.
362+
Use the [Ollama Docker GPU setup instructions](https://github.com/ollama/ollama/blob/main/docs/docker.md){:target="_blank"} as a starting point.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
layout: docwithnav
3+
title: Securing Ollama with an Nginx Reverse Proxy
4+
description: Secure your local Ollama LLM deployment with Nginx and Docker Compose. This step-by-step guide provides copy-paste commands to easily set up username/password or API key authentication.
5+
---
6+
7+
{% include get-hosts-name.html docsPrefix=docsPrefix %}
8+
{% include /docs/samples/analytics/ollama/nginx.md %}

0 commit comments

Comments
 (0)