Arama Yap Mesaj Submit
Request a Callback
+90
X
X

Select Your Currency

Turkish Lira $ US Dollar Euro
X
X

Select Your Currency

Turkish Lira $ US Dollar Euro

Contact Us

Location Halkali merkez neighborhood fatih st ozgur apt no 46 , Kucukcekmece , Istanbul , 34303 , TR
Ubuntu 24.04 Self-Hosted AI Stack: Docker, Portainer, Ollama, Open WebUI and n8n
Self-Hosted AI, Ubuntu, Docker and n8n

Complete Self-Hosted AI Stack on Ubuntu 24.04 VPS: Docker + Portainer + Ollama + Open WebUI + n8n Illustrated Guide

This pillar guide combines the full environment we built on a real VPS upgraded from Ubuntu 22.04 to Ubuntu 24.04 LTS: Docker + Portainer, Ollama qwen3:4b, Open WebUI and an n8n AI Agent behind Nginx and TLS. It documents not only successful screens but also real fixes for exit code 141, Portainer timeout, Cloudflare 521, Certbot timeout, Open WebUI health delay, Ollama credentials and the old HTTP Tool node.

Ubuntu 24.04Self Hosted AIDockerDocker ComposePortainerOllamaqwen3:4bOpen WebUIn8nAI AgentNginxLet’s EncryptLocal LLMQwen3RAGQdrantEKA Sunucu
Ubuntu / Docker / Portainer / Ollama / Open WebUI / n8n
Ubuntu 24.04.4 LTS
  ↓
Docker + eka-ai
  ├─ Portainer CE LTS
  ├─ Ollama 0.32.6 → qwen3:4b
  ├─ Open WebUI 0.11.0
  └─ n8n 2.33.7 → AI Agent + Memory + Tools
  ↓
Nginx + Let’s Encrypt + EKA Sunucu domains
StackSelf-Hosted AIModelqwen3:4b
24real WebP screenshots
3TR · EN · DE content
443public HTTPS
127.0.0.1AI service loopback
01Ubuntu 24.04 + Docker foundation
02Portainer + Nginx + TLS
03Ollama + Qwen3 + Open WebUI
04n8n AI Agent + Tools + next RAG
00
Table of contents

Complete self-hosted AI stack deployment steps

  1. 01What exactly is the self-hosted AI stack we built on an Ubuntu VPS?
  2. 02Plan CPU, RAM, storage and GPU availability before deployment
  3. 03Upgrade Ubuntu 22.04 LTS safely to Ubuntu 24.04 LTS
  4. 04Install Docker Engine and Docker Compose from Docker’s official APT repository
  5. 05Install Portainer CE LTS with persistent storage and bind 9443 to localhost
  6. 06Add Nginx reverse proxy, Cloudflare-aware DNS and Let’s Encrypt certificates
  7. 07Create the shared eka-ai network for Ollama, Open WebUI and n8n
  8. 08Run Ollama in Docker and expose its API only on localhost:11434
  9. 09Pull qwen3:4b and run a real inference test
  10. 10Do not expose Ollama port 11434 directly to the public Internet
  11. 11Connect Open WebUI to Ollama and interpret slow first startup correctly
  12. 12Publish openwebui.ekasunucu.com through Nginx and TLS
  13. 13Deploy n8n with persistent data and a fixed encryption key
  14. 14Complete reverse proxy, WebSocket and TLS for n8n.ekasunucu.com
  15. 15Use http://ollama:11434 as the Ollama credential Base URL inside n8n
  16. 16Build Chat Trigger + AI Agent + qwen3:4b + Simple Memory
  17. 17Validate Calculator and HTTP Request Tool so the agent can perform actions
  18. 18Verify the full stack with reboot, health, ports, volumes and certificate renewal
  19. 19Next: add Qdrant + embeddings + RAG for private-document answers
01
Complete architecture

What exactly is the self-hosted AI stack we built on an Ubuntu VPS?

This pillar guide brings together the complete stack we built on one real Ubuntu VPS: Ubuntu 24.04 LTS, Docker Engine + Compose, Portainer, Ollama, qwen3:4b, Open WebUI, n8n, Nginx and Let’s Encrypt. Application services run in separate Docker containers and communicate over a private user-defined bridge network named eka-ai.

Only Nginx exposes public HTTP/HTTPS on ports 80 and 443. Portainer 9443, Ollama 11434, Open WebUI 3000 and n8n 5678 are bound to 127.0.0.1 on the host. Management and AI services therefore are not directly exposed; domain access is provided through reverse proxy and TLS.

Command 1
Internet :443
  ↓
Nginx + Let's Encrypt
  ├─ portainer.ekasunucu.com → 127.0.0.1:9443
  ├─ openwebui.ekasunucu.com → 127.0.0.1:3000
  └─ n8n.ekasunucu.com → 127.0.0.1:5678

Docker eka-ai
  ├─ Portainer
  ├─ Ollama → qwen3:4b
  ├─ Open WebUI → ollama:11434
  └─ n8n AI Agent → ollama:11434
02
VPS resources

Plan CPU, RAM, storage and GPU availability before deployment

Our real test VPS had 8 vCPU, about 31 GiB RAM and a 99 GB root disk. The CPU appeared as AMD Ryzen 9 7950X. No dedicated NVIDIA or ROCm GPU passthrough was available, so Ollama ran in CPU mode. qwen3:4b worked functionally in this environment, although response latency is naturally higher than on a suitable GPU system.

As model size grows, RAM, disk and potentially VRAM requirements increase. Open WebUI may also download embedding and cache data during first startup, so size the server for Docker images, named volumes, execution data and caches in addition to the LLM itself.

Command 1
lscpu | grep -E 'Model name|CPU\(s\)'
free -h
df -h /
nvidia-smi || true
ls -la /dev/dri 2>/dev/null || true
03
Operating system

Upgrade Ubuntu 22.04 LTS safely to Ubuntu 24.04 LTS

If the VPS still runs Ubuntu 22.04, fully update the current release and take a backup or snapshot first. Ubuntu Server uses do-release-upgrade for major release upgrades and the flow is interactive. In our real upgrade we saw recovery SSH on port 1022, the package plan, an openssh-server configuration prompt, obsolete-package cleanup and the final reboot.

After reboot, verify /etc/os-release, the kernel, APT status and failed systemd units. Our final environment completed on Ubuntu 24.04.4 LTS, Noble and kernel 6.8.0-137-generic.

Command 1
apt update
apt full-upgrade -y
reboot
Command 2
do-release-upgrade
Command 3
cat /etc/os-release
uname -r
apt update
systemctl --failed
04
Container platform

Install Docker Engine and Docker Compose from Docker’s official APT repository

Using Docker’s official Ubuntu APT repository gives a predictable Engine and Compose plugin setup. The real environment used Docker Engine 29.7.2 and Docker Compose v5.4.0; these are recorded test versions, not a claim about the latest release.

Our first automation script exited with code 141 because set -o pipefail was combined with apt-cache policy piped into head, causing SIGPIPE. Docker itself was not broken. Replacing head with sed allowed the script to continue. We then validated Docker with the hello-world container.

Command 1
docker --version
docker compose version
systemctl is-active docker
Command 2
docker run --rm hello-world
05
Container management

Install Portainer CE LTS with persistent storage and bind 9443 to localhost

We created the portainer_data named volume and mounted the Docker socket into Portainer. The management UI uses HTTPS on port 9443 inside the container. During first setup we encountered the security timeout/setup-token flow, restarted the container and completed the admin account.

For the final deployment, host port 9443 was bound to 127.0.0.1 instead of 0.0.0.0. Remote access is provided only through portainer.ekasunucu.com behind Nginx and HTTPS.

Command 1
docker volume create portainer_data
Command 2
docker run -d --name portainer --restart=always -p 127.0.0.1:9443:9443 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:lts
Command 3
docker ps --filter name='^/portainer$'
06
Domains and TLS

Add Nginx reverse proxy, Cloudflare-aware DNS and Let’s Encrypt certificates

Before the reverse proxy existed, the Portainer domain returned Cloudflare 521. After creating the Nginx server block and pointing upstream traffic to 127.0.0.1:9443, origin access worked. During certificate setup the Certbot snap initially timed out while downloading core24; a later retry succeeded once network access recovered.

We reused the same pattern for Open WebUI and n8n. Preserve Upgrade and Connection headers for WebSocket applications. After issuing certificates, run certbot renew --dry-run to test automated renewal.

Command 1
nginx -t
systemctl reload nginx
Command 2
certbot --nginx -d portainer.ekasunucu.com
Command 3
certbot renew --dry-run
07
Docker service network

Create the shared eka-ai network for Ollama, Open WebUI and n8n

Containers must not use host localhost to reach another container. We created a user-defined bridge network named eka-ai. Because the Ollama container is named ollama, Open WebUI and n8n can reach it through http://ollama:11434 using Docker DNS.

Named volumes ollama_data, openwebui_data and n8n_data keep important data outside container lifecycles. Plan backups before image updates or container recreation.

Command 1
docker network create eka-ai 2>/dev/null || true
Command 2
docker volume create ollama_data
docker volume create openwebui_data
docker volume create n8n_data
Command 3
docker network inspect eka-ai
08
Local LLM runtime

Run Ollama in Docker and expose its API only on localhost:11434

We ran the official Ollama image and attached /root/.ollama to the ollama_data volume. The test VPS had no GPU passthrough, so Ollama selected the CPU backend. Although Ollama listens on 0.0.0.0:11434 inside the container, the host mapping is 127.0.0.1:11434:11434, preventing direct public exposure.

The first /api/tags health check returned HTTP 200 with an empty model list. This is useful because it separates runtime availability from model-download problems.

Command 1
docker pull ollama/ollama:latest
Command 2
docker run -d --name ollama --restart=always --network eka-ai -p 127.0.0.1:11434:11434 -v ollama_data:/root/.ollama ollama/ollama:latest
Command 3
curl -sS http://127.0.0.1:11434/api/tags
09
Model installation

Pull qwen3:4b and run a real inference test

In our real deployment qwen3:4b downloaded at roughly 2.5 GB. Ollama metadata reported the 4.0B parameter class, Q4_K_M quantization and tools/thinking capabilities. Loaded memory usage reached roughly 3 GiB in the CPU test.

Do not stop at ollama list. We sent a real /api/chat request and received EKA-OLLAMA-TEST-BASARILI. That test later helped us distinguish Open WebUI or n8n integration issues from the underlying model runtime.

Command 1
docker exec ollama ollama pull qwen3:4b
Command 2
docker exec ollama ollama list
Command 3
curl -sS http://127.0.0.1:11434/api/chat -H 'Content-Type: application/json' -d '{"model":"qwen3:4b","messages":[{"role":"user","content":"Only write EKA-OLLAMA-TEST-BASARILI"}],"stream":false}'
10
API security

Do not expose Ollama port 11434 directly to the public Internet

The default local Ollama API is not by itself a complete public-user authentication layer. In this architecture host port 11434 listens only on 127.0.0.1. Open WebUI and n8n reach Ollama privately over Docker DNS on the eka-ai network.

If remote systems must access Ollama, add authentication, TLS, reverse proxy, VPN or a private network instead of simply publishing 11434. Verify the socket with ss.

Command 1
ss -lntp | grep 11434
Command 2
docker network inspect eka-ai --format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{println}}{{end}}'
11
Chat interface

Connect Open WebUI to Ollama and interpret slow first startup correctly

Open WebUI was started with persistent openwebui_data, a fixed WEBUI_SECRET_KEY and the eka-ai network. The host port was restricted to 127.0.0.1:3000 and the Ollama provider pointed to http://ollama:11434.

The /health endpoint did not immediately return 200. The container was running database migrations and downloading sentence-transformers/all-MiniLM-L6-v2 files from Hugging Face. After the initial preparation period, health returned 200 and the container became healthy. A short first-start health timeout therefore does not automatically mean the install failed.

Command 1
docker run -d --name open-webui --restart=always --network eka-ai -p 127.0.0.1:3000:8080 -e OLLAMA_BASE_URL=http://ollama:11434 -v openwebui_data:/app/backend/data ghcr.io/open-webui/open-webui:main
Command 2
curl -sS http://127.0.0.1:3000/health
12
Open WebUI HTTPS

Publish openwebui.ekasunucu.com through Nginx and TLS

Once Open WebUI was healthy, Nginx proxied openwebui.ekasunucu.com to localhost:3000 with WebSocket headers. Let’s Encrypt deployed the certificate successfully and both origin HTTPS and domain HTTPS returned 200.

The first Open WebUI account became administrator. Seeing qwen3:4b in the model selector visually confirmed that Open WebUI could reach Ollama.

Command 1
nginx -t
systemctl reload nginx
Command 2
certbot --nginx -d openwebui.ekasunucu.com
Command 3
curl -I https://openwebui.ekasunucu.com
13
Automation layer

Deploy n8n with persistent data and a fixed encryption key

We created n8n_data and a stable N8N_ENCRYPTION_KEY. n8n joined eka-ai and exposed its host port only at 127.0.0.1:5678. Host, protocol, editor URL, proxy/webhook and timezone values were stored in the environment file for reverse-proxy operation.

The tested n8n version was 2.33.7 and /healthz returned status ok. Startup logs contained Python task-runner and future-configuration warnings, but they did not block the Chat Trigger + AI Agent workflow used here.

Command 1
docker volume create n8n_data
Command 2
openssl rand -hex 32
Command 3
docker run -d --name n8n --restart=always --network eka-ai --env-file /root/n8n.env -p 127.0.0.1:5678:5678 -v n8n_data:/home/node/.n8n docker.n8n.io/n8nio/n8n:latest
Command 4
curl -sS http://127.0.0.1:5678/healthz
14
n8n HTTPS

Complete reverse proxy, WebSocket and TLS for n8n.ekasunucu.com

n8n port 5678 was not published directly to the Internet. Nginx proxied n8n.ekasunucu.com to localhost:5678 with forwarded and WebSocket headers. After Let’s Encrypt setup the domain returned HTTPS 200.

Use ss to verify that 5678, 11434, 3000 and 9443 remain loopback-only while Nginx owns public 80/443.

Command 1
certbot --nginx -d n8n.ekasunucu.com
Command 2
ss -lntp | grep -E ':80 |:443 |:9443 |:11434 |:3000 |:5678 '
15
Container DNS

Use http://ollama:11434 as the Ollama credential Base URL inside n8n

Because n8n and Ollama are separate containers, localhost:11434 inside n8n points back to n8n itself. Both containers share eka-ai, so we used http://ollama:11434. A request from inside the n8n container to /api/tags returned HTTP 200 and qwen3:4b was present.

The first imported workflow lacked an Ollama credential reference, so the Ollama Qwen3 4B sub-node failed. After creating the credential with the correct Docker DNS address, the workflow succeeded.

Command 1
docker exec n8n node -e "fetch('http://ollama:11434/api/tags').then(r=>r.text()).then(console.log)"
16
Local AI Agent

Build Chat Trigger + AI Agent + qwen3:4b + Simple Memory

The main workflow connects Chat Trigger to AI Agent. Ollama qwen3:4b is attached to the Chat Model input and Simple Memory to the Memory input. The system message explicitly identifies the real runtime model as qwen3:4b so the model does not invent a deployment/model name.

In the final execution Chat Trigger, AI Agent, Ollama Chat Model and Memory all completed successfully. The agent correctly stated that it runs through Ollama with qwen3:4b.

Command 1
Prompt: {{ $json.chatInput }}
Command 2
Ollama Base URL: http://ollama:11434
Model: qwen3:4b
17
Agent tools

Validate Calculator and HTTP Request Tool so the agent can perform actions

We connected Calculator to the AI Agent Tool input. For 3478 × 129 the agent called the tool and returned the deterministic result 448662 rather than relying on mental arithmetic.

The first HTTP test used an older @n8n/n8n-nodes-langchain.toolHttpRequest node type and failed with “supplyData method but no execute method”. Switching to the current n8n HTTP Request Tool fixed the execution. The agent fetched a real JSONPlaceholder TODO response and summarized the JSON in Turkish.

Command 1
3478 × 129 kaç eder? Hesap makinesini kullan.
Command 2
OrnekAPIVeriGetir aracını kullan ve gelen API verisini Türkçe açıkla.
18
Production validation

Verify the full stack with reboot, health, ports, volumes and certificate renewal

A complete validation goes beyond browser screenshots. Check Docker container status, local health endpoints, public domain status, loopback port bindings, named volumes, failed systemd units and Certbot renewal. Reboot the VPS and confirm restart policies bring the containers back.

Back up portainer_data, ollama_data, openwebui_data and n8n_data together with Nginx site files, Let’s Encrypt data and the n8n encryption key. LLM model files can be re-downloaded, but n8n credentials/workflows and Open WebUI user data are usually more valuable.

Command 1
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
Command 2
curl -fsS http://127.0.0.1:11434/api/tags >/dev/null && echo OLLAMA_OK
curl -fsS http://127.0.0.1:3000/health >/dev/null && echo WEBUI_OK
curl -fsS http://127.0.0.1:5678/healthz >/dev/null && echo N8N_OK
Command 3
certbot renew --dry-run
Command 4
systemctl --failed
19
RAG and knowledge

Next: add Qdrant + embeddings + RAG for private-document answers

At this point the infrastructure layers are ready: Portainer manages containers, Ollama provides the local LLM, Open WebUI provides browser chat and n8n provides automation and agent orchestration. The next logical step is a vector database such as Qdrant plus an embedding model to build RAG over your own documents.

The RAG guide will separate document ingestion, chunking, embeddings, vector storage, retrieval and source-aware answers. qwen3:4b can then work with your current private knowledge base instead of relying only on its general model knowledge.

Command 1
Docker / eka-ai
  ├─ Ollama → qwen3:4b
  ├─ Open WebUI
  ├─ n8n AI Agent
  └─ Qdrant + Embeddings + RAG  ← next guide
Production checklist

Production security checklist for the self-hosted AI stack

Do not expose service ports 9443, 11434, 3000 or 5678 directly to the public Internet.
Publish public access through Nginx on 443 with valid TLS certificates.
Use strong unique administrator passwords in Portainer, Open WebUI and n8n.
Back up fixed secrets such as N8N_ENCRYPTION_KEY and WEBUI_SECRET_KEY and avoid unnecessary disclosure.
Back up Docker named volumes and take snapshots before image updates.
If external Ollama API access is required, prefer VPN, private networking or an authenticated proxy.
Test WebSocket and forwarded-header configuration for n8n and Open WebUI.
Run certbot renew --dry-run and monitor certificate expiry.
Review docker logs, docker stats and systemctl --failed regularly.
Apply least privilege to n8n HTTP Request, Code and community nodes.
Verify node typeVersion compatibility when importing old workflows into a new n8n release.
After a VPS reboot, re-run container, domain and AI inference checks.
R
Official sources

Official Ubuntu, Docker, Portainer, Ollama, Open WebUI and n8n resources

+
EKA Sunucu

Detailed EKA Sunucu guides behind this pillar article

?
FAQ

Frequently asked questions about the Ubuntu self-hosted AI stack

Does this stack run entirely on my own VPS?

Yes. Ollama, Open WebUI and n8n run as Docker containers on your Ubuntu VPS. Workflows or optional model features that call external services can still create outbound connections.

Can Ollama run without a GPU?

Yes. Our real test VPS had no dedicated GPU passthrough and qwen3:4b ran on CPU. A suitable GPU provides much better performance for heavier workloads.

Why Ubuntu 24.04 LTS?

The tested machine was upgraded from Ubuntu 22.04 to Ubuntu 24.04.4 LTS and the complete container stack was validated on that environment.

Is Docker Compose required?

Most services were validated with docker run while the Compose plugin was also installed. Compose is useful for repeatable multi-container production management.

Should Portainer 9443 be public?

Not in this architecture. It is bound to 127.0.0.1 and exposed remotely only through the Nginx HTTPS domain.

Should Ollama 11434 be open to the Internet?

No. A safer default is loopback/private Docker networking unless you add a deliberate authentication and network-security layer.

Why can Open WebUI take time on first startup?

Initial database migrations and embedding/cache downloads can delay readiness. In our test /health became 200 after the first preparation period.

What Ollama URL should Open WebUI use?

When both containers share eka-ai, use http://ollama:11434.

Why is n8n Ollama Base URL not localhost?

localhost inside the n8n container means the n8n container itself. Use the Ollama container DNS name on the shared Docker network.

Did qwen3:4b work with tools?

Yes in this tested workflow: Calculator and HTTP Request Tool both executed successfully and Ollama metadata exposed tools capability.

What was the Calculator test result?

3478 × 129 executed through Calculator Tool and returned 448662.

Why did the first HTTP Tool fail?

An older LangChain HTTP tool node type was incompatible with the running n8n version. The current HTTP Request Tool fixed the execution.

Do TLS certificates renew automatically?

Certbot installs scheduled renewal, but you should still periodically run certbot renew --dry-run.

What data should I back up?

Prioritize portainer_data, openwebui_data, n8n_data, the n8n encryption key, Nginx/Let’s Encrypt configuration and ollama_data when model data is expensive to re-download.

What is the next step?

Add Qdrant, an embedding model and a RAG workflow so your AI Agent can answer from your own documents.

Can I deploy this on an EKA Sunucu VPS?

Yes when the Linux VPS has enough CPU, RAM and storage. Model size and concurrent usage determine the actual resource requirement.

EKA YAZILIM VE BİLİŞİM SİSTEMLERİ

Need a Linux VPS for your own self-hosted AI stack?

Run Docker, Portainer, Ollama, Open WebUI, n8n, Qdrant and local AI models on your own server with an EKA Sunucu Linux VPS sized for your workload.

Updated: 10.08.2026
View Linux VPS PlansLinux & VPS Guides
Top