วิธีรัน Ollama ด้วย Podman แบบ rootless บน VPS
เรียนรู้วิธีรัน Ollama ใน Podman แบบ rootless บน VPS อย่างปลอดภัย ตั้งแต่การตั้งค่าผู้ใช้ การใช้ Quadlet เพื่อจัดการคอนเทนเนอร์หลังรีบูต การตั้งค่า SELinux และการทำ SSH tunnel
การรัน Ollama ใน Podman แบบ rootless บน VPS
การรัน Ollama ใน Podman แบบ rootless บนเซิร์ฟเวอร์จำเป็นต้องมีเงื่อนไข 5 ประการที่คู่มือการใช้งานบนเดสก์ท็อปทั่วไปมักข้ามไป ประการแรกคือต้องมีผู้ใช้ที่ไม่มีสิทธิ์ (unprivileged user) เป็นเจ้าของคอนเทนเนอร์ ประการที่สองคือต้องเปิดใช้งาน lingering สำหรับผู้ใช้นั้น เพื่อให้คอนเทนเนอร์ทำงานต่อไปได้หลังจากที่คุณออกจากระบบ ประการที่สามคือต้องใช้ไฟล์ Quadlet เพื่อส่งมอบคอนเทนเนอร์ให้ systemd ดูแล เพื่อให้คอนเทนเนอร์กลับมาทำงานใหม่หลังรีบูต ประการที่สี่คือไดเรกทอรีโมเดลต้องมี SELinux label ที่ถูกต้องบนดิสทริบิวชันที่มีการบังคับใช้ และประการสุดท้ายคือ API ต้องฟังเฉพาะที่ loopback เท่านั้น โดยคุณจะเข้าถึงผ่าน SSH (secure shell) tunnel
Ollama เป็นเซิร์ฟเวอร์สำหรับโมเดลภาษาขนาดใหญ่ (LLM) โดยจะจัดเก็บน้ำหนักของโมเดลไว้ในดิสก์ โหลดเข้าสู่หน่วยความจำ และตอบสนองคำขอ HTTP บนพอร์ต 11434 เนื่องจากไม่มีระบบล็อกอิน ไม่มี API key และไม่มีบัญชีผู้ใช้ เครือข่ายจึงเป็นกลไกควบคุมการเข้าถึงเพียงอย่างเดียวที่คุณมี Podman รันคอนเทนเนอร์โดยไม่มี daemon และไม่มีสิทธิ์ root ดังนั้นหากมีสิ่งใดหลุดรอดออกมาจากคอนเทนเนอร์ สิ่งนั้นจะเริ่มต้นด้วยสิทธิ์ของผู้ใช้ทั่วไปที่ไม่มีสิทธิ์ หากคุณต้องการเปรียบเทียบรันไทม์ก่อน โปรดอ่าน ความแตกต่างระหว่าง Podman และ Docker บน VPS หากคุณต้องการข้ามการใช้คอนเทนเนอร์ไปเลย การติดตั้ง Ollama โดยตรงบน VPS เป็นวิธีที่สั้นกว่า
SSD Nodes มี Fedora เป็นหนึ่งในอิมเมจที่ให้บริการ และ Fedora มาพร้อมกับ Podman และ SELinux (security-enhanced Linux) เป็นค่าเริ่มต้น คำสั่งทั้งหมดด้านล่างนี้สามารถรันได้บนดิสทริบิวชันใดก็ตามที่ใช้ Podman เวอร์ชัน 5 ขึ้นไป
เหตุผลที่เวอร์ชันสำหรับแล็ปท็อปต้องมีการปรับเปลี่ยนเมื่อนำมาใช้บนเซิร์ฟเวอร์
Fedora Magazine ได้เผยแพร่บทความแนะนำการใช้งาน stack นี้อย่างชัดเจนเมื่อวันที่ 5 สิงหาคม 2026 ในหัวข้อ Running Ollama Locally with Podman on Fedora Linux โดย Yazan Monshed ซึ่งเป็นจุดเริ่มต้นที่ดีในการทำความรู้จักกับเครื่องมือเหล่านี้ อย่างไรก็ตาม บทความดังกล่าวมุ่งเน้นไปที่การใช้งานบนแล็ปท็อป และมี 4 ประเด็นที่ส่งผลแตกต่างออกไปเมื่อนำไปใช้บนเครื่องที่มี Public IP address:
- การเริ่ม container ด้วยคำสั่ง
podman run -dแบบปกติ: container ที่เริ่มด้วยมือจะไม่กลับมาทำงานใหม่หลังจากรีบูตเครื่อง เพราะไม่มีการกำหนดค่าให้ระบบเริ่มทำงานโดยอัตโนมัติ - การใช้ tag แบบเคลื่อนไหวอย่าง
ollama/ollama: บนแล็ปท็อป คุณจะสังเกตเห็นได้ทันทีในวันที่พฤติกรรมของซอฟต์แวร์เปลี่ยนไป แต่บนเซิร์ฟเวอร์ สัญญาณแรกที่คุณจะพบคือสคริปต์ที่หยุดทำงานไปเองในชั่วข้ามคืน - การเผยแพร่บริการด้วย
-p 11434:11434ซึ่งเป็นการผูก (bind) เข้ากับทุก interface: หากอยู่หลัง home router บริการนี้จะไม่สามารถเข้าถึงได้จากอินเทอร์เน็ต แต่บน VPS นี่คือการเปิด API สำหรับ inference สู่สาธารณะโดยไม่มีการตั้งรหัสผ่านป้องกัน - การรันด้วยสิทธิ์ของผู้ใช้ที่คุณล็อกอินอยู่: บนเซิร์ฟเวอร์ บัญชีผู้ใช้ที่เป็นเจ้าของ container ไม่ควรเป็นเจ้าของไฟล์อื่นใด เพื่อป้องกันกรณีที่หากเกิดการเจาะระบบ (break-out) ผู้บุกรุกจะพบเพียง home directory ที่ว่างเปล่าเท่านั้น
สิ่งที่กล่าวมาข้างต้นไม่ใช่เรื่องผิดสำหรับการใช้งานบนเครื่องที่บทความตั้งใจไว้ แต่ละประเด็นเป็นเพียงการตัดสินใจที่คุณต้องพิจารณาใหม่เมื่อเครื่องเซิร์ฟเวอร์ของคุณสามารถเข้าถึงได้จากทุกที่และไม่มีใครนั่งเฝ้าอยู่หน้าเครื่องตลอดเวลา
สร้างผู้ใช้ที่ไม่มีสิทธิ์ (unprivileged user) และตรวจสอบ subuid
Rootless Podman จะแมป UID ภายในคอนเทนเนอร์เข้ากับช่วงของ ID ที่ไม่ได้ใช้งานบนโฮสต์ ช่วงดังกล่าวถูกประกาศไว้ใน /etc/subuid และ /etc/subgid หากไม่มีการตั้งค่านี้ คอนเทนเนอร์แบบ rootless จะไม่สามารถเริ่มทำงานได้เลย
sudo dnf install -y podman # or: sudo apt install -y podman
sudo useradd --create-home --shell /bin/bash --comment "Ollama container owner" ollama
sudo passwd --lock ollama
grep ollama /etc/subuid /etc/subgidคำสั่ง grep ควรแสดงผลลัพธ์สองบรรทัด โดยมาจากแต่ละไฟล์ และแต่ละบรรทัดต้องระบุช่วงของ ID จำนวน 65536 ค่า:
/etc/subuid:ollama:100000:65536
/etc/subgid:ollama:100000:65536ตัวเลขเริ่มต้นของคุณอาจแตกต่างกันไปซึ่งถือเป็นเรื่องปกติ หาก grep ไม่แสดงผลลัพธ์ใดๆ แสดงว่า useradd ไม่ได้จัดสรรช่วง ID ไว้ และคำสั่ง podman แรกในฐานะผู้ใช้นั้นจะล้มเหลวในลักษณะนี้:
Error: cannot find UID/GID for user ollama: no subuid ranges found for user "ollama" in /etc/subuidให้กำหนดช่วง ID ที่ไม่มีผู้ใช้อื่นใช้งานอยู่ จากนั้นแจ้งให้ Podman ทราบว่าการแมปเดิมนั้นล้าสมัยแล้ว:
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 ollama
sudo -iu ollama podman system migrateการล็อกรหัสผ่านหมายความว่าจะไม่มีใครสามารถล็อกอินเป็น ollama ได้โดยตรง คุณสามารถเข้าถึงบัญชีนี้ได้จากผู้ดูแลระบบของคุณด้วยคำสั่ง sudo -iu ollama
เปิดใช้งาน lingering เพื่อให้ service ทำงานต่อไปได้หลังออกจากระบบ
โดยปกติแล้ว instance ของ systemd สำหรับผู้ใช้จะเริ่มทำงานเมื่อเข้าสู่ระบบและหยุดทำงานเมื่อออกจากระบบ ซึ่ง /run/user/<uid> จะถูกลบออกไปพร้อมกัน ส่งผลให้ container แบบ rootless ทั้งหมดที่ผู้ใช้เป็นเจ้าของจะหยุดทำงานในทันที การเปิดใช้งาน lingering จะช่วยให้ instance ของผู้ใช้ยังคงทำงานต่อไปได้แม้ไม่มี session เชื่อมต่ออยู่
sudo loginctl enable-linger ollama
loginctl show-user ollama --property=Lingerคำสั่งดังกล่าวควรแสดงผลเป็น Linger=yes ให้เปิดใช้งานก่อนที่จะสร้าง unit เนื่องจากไดเรกทอรีที่ unit จำเป็นต้องใช้คือ /run/user/<uid> จะถูกสร้างขึ้นก็ต่อเมื่อเปิดใช้งาน lingering แล้วเท่านั้น
ยังมีอีกหนึ่งขั้นตอนที่มักถูกมองข้าม การใช้ sudo -iu ollama จะทำให้คุณได้ shell แต่ไม่ได้ session bus ส่งผลให้ systemctl --user ล้มเหลวในทันที:
Failed to connect to bus: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not definedsystemd จะค้นหา user bus ที่ $XDG_RUNTIME_DIR/bus แต่ sudo -i ไม่ได้ตั้งค่าตัวแปรดังกล่าวไว้ คุณต้องตั้งค่าด้วยตนเองในทุก admin shell ที่คุณใช้จัดการ service นี้:
sudo -iu ollama
export XDG_RUNTIME_DIR=/run/user/$(id -u)
systemctl --user statusตำแหน่งที่เก็บ model blobs และการวางแผนพื้นที่ดิสก์
Ollama จะเขียนไฟล์น้ำหนัก (weights) ลงใน /root/.ollama/models ภายในคอนเทนเนอร์ หากคุณทำการ bind directory จากโฮมไดเรกทอรีของผู้ใช้ไปยัง path ดังกล่าว ไฟล์จะถูกจัดเก็บในตำแหน่งที่คุณสามารถตรวจสอบขนาดได้คือ /home/ollama/ollama-data/models โดยไฟล์ blobs จะถูกเก็บไว้ใน models/blobs ในรูปแบบไฟล์ที่อ้างอิงตามเนื้อหา (content-addressed) และ models/manifests จะเป็นที่เก็บดัชนีขนาดเล็กที่ใช้ระบุชื่อไฟล์เหล่านั้น หากคุณใช้ named volume แทนตามที่ระบุในบทความของ Fedora Magazine โครงสร้างไฟล์เดียวกันนี้จะอยู่ใน /home/ollama/.local/share/containers/storage/volumes/<volume>/_data
ควรประเมินขนาดดิสก์ก่อนที่จะทำการดึง (pull) ข้อมูลใดๆ ขนาดไฟล์ดาวน์โหลดที่ประกาศไว้คือค่าต่ำสุดที่คุณต้องเตรียม
The data behind this chart
[
{
"label": "gemma3:4b",
"download_gb": 3.3
},
{
"label": "mistral:7b",
"download_gb": 4.4
},
{
"label": "qwen3:8b",
"download_gb": 5.2
},
{
"label": "gemma3:12b",
"download_gb": 8.1
},
{
"label": "qwen3:14b",
"download_gb": 9.3
},
{
"label": "gemma3:27b",
"download_gb": 17
},
{
"label": "qwen3:30b",
"download_gb": 19
}
]ข้อมูลทั้ง 7 แถวนี้เป็นตัวเลขที่เผยแพร่บน ollama.com/library ไม่ใช่ขนาดที่วัดจริงบนดิสก์ แท็กที่เล็กที่สุดในที่นี้คือ gemma3:4b ซึ่งมีขนาดดาวน์โหลด 3.3 GB ส่วนแท็กที่ใหญ่ที่สุดคือ qwen3:30b มีขนาดดาวน์โหลด 19 GB ตัวคอนเทนเนอร์อิมเมจเองจะถูกเก็บแยกต่างหากในพื้นที่จัดเก็บของ Podman ดังนั้นควรตรวจสอบตัวเลขทั้งสองส่วนร่วมกันด้วย podman system df และ df -h /home นอกจากนี้ โมเดลยังต้องการพื้นที่ใน RAM ประมาณเท่ากับขนาดไฟล์ของตัวมันเองในขณะที่ถูกโหลดขึ้นใช้งาน บวกกับพื้นที่สำหรับ context window ดังนั้นโมเดลขนาด 19 GB จะไม่สามารถทำงานบน VPS ที่มี RAM 16 GB ได้
กำหนดเวอร์ชันของ image tag ให้ชัดเจน และระบุชื่อ registry ให้ครบถ้วน
sudo -iu ollama
export XDG_RUNTIME_DIR=/run/user/$(id -u)
mkdir -p ~/ollama-data ~/.config/containers/systemd
podman pull docker.io/ollama/ollama:0.32.9ให้ใช้ tag ของเวอร์ชันที่ปล่อยออกมาแล้ว เช่น 0.32.9 ณ เดือนสิงหาคม 2026 และไม่ควรใช้ latest การกำหนด tag ที่แน่นอนหมายความว่าเมื่อมีการรีสตาร์ทระบบในเวลา 04:00 น. คุณจะได้ binary ตัวเดิมที่คุณเคยทดสอบไว้ ดังนั้นหากพฤติกรรมของระบบเปลี่ยนไป นั่นหมายถึงคุณเป็นผู้เปลี่ยนแปลงเอง Docker Hub ยังมีการเผยแพร่ tag -rc และ -rocm สำหรับเวอร์ชันเดียวกัน ให้เลือกใช้ tag แบบปกติเว้นแต่ว่าคุณจะใช้ AMD GPU
ระบุชื่อโฮสต์ของ registry ด้วย บน Fedora หากใช้ชื่อสั้นใน systemd unit จะไม่มีเทอร์มินัลสำหรับแสดงข้อความแจ้งเตือน และ unit จะล้มเหลวด้วยข้อความ:
Error: short-name "ollama/ollama" did not resolve to an alias and no unqualified-search registries are definedการดึง image ด้วยตนเองก่อนเป็นทางเลือกเสริมแต่มีประโยชน์ เพราะจะช่วยย้ายขั้นตอนการดาวน์โหลดขนาดหลายกิกะไบต์ออกไปจากช่วงเวลา timeout ของการเริ่มทำงานของ unit
Quadlet unit ที่คงอยู่หลังการรีบูต
Quadlet คือตัวสร้าง systemd ของ Podman คุณเขียนไฟล์ .container แล้ว systemd จะเปลี่ยนมันให้เป็น service ในตอนบูต โดยไม่จำเป็นต้องใช้ podman generate systemd อีกต่อไป ให้บันทึกไฟล์นี้เป็น /home/ollama/.config/containers/systemd/ollama.container โดยมีผู้ใช้ ollama เป็นเจ้าของ
[Unit]
Description=Ollama API (rootless)
After=network-online.target
Wants=network-online.target
[Container]
Image=docker.io/ollama/ollama:0.32.9
ContainerName=ollama
PublishPort=127.0.0.1:11434:11434
Volume=/home/ollama/ollama-data:/root/.ollama:Z
Environment=OLLAMA_KEEP_ALIVE=30m
Environment=OLLAMA_MAX_LOADED_MODELS=1
[Service]
Restart=always
TimeoutStartSec=900
[Install]
WantedBy=default.targetชื่อไฟล์จะเป็นตัวกำหนดชื่อ service ดังนั้น ollama.container จะกลายเป็น ollama.service
systemctl --user daemon-reload
systemctl --user start ollama.service
systemctl --user status ollama.servicestatus ควรแสดงผลเป็น active (running) ห้ามรันคำสั่ง systemctl --user enable ollama.service เนื่องจาก unit นี้ไม่ได้มีตัวตนเป็นไฟล์อยู่บนดิสก์ systemd จึงปฏิเสธการทำงาน:
Failed to enable unit: Unit file /run/user/1001/systemd/generator/ollama.service is transient or generated.ส่วน [Install] ทำหน้าที่นั้นอยู่แล้ว Quadlet จะสร้างลิงก์สำหรับเริ่มทำงานตอนบูตด้วยตัวเองในระหว่างขั้นตอน daemon-reload ซึ่งเป็นเหตุผลว่าทำไมคำสั่งนั้นจึงไม่สามารถละเว้นได้ TimeoutStartSec=900 ครอบคลุมถึงการเริ่มทำงานครั้งแรกที่ยังต้องดึง image มาก่อน เนื่องจากค่าเริ่มต้น 90 วินาทีไม่เพียงพอสำหรับการดาวน์โหลดขนาด 2 กิกะไบต์ และ systemd จะสั่งยุติการทำงานเพราะถือว่าล้มเหลว OLLAMA_KEEP_ALIVE=30m จะคงโมเดลไว้ในหน่วยความจำระหว่างการร้องขอ แทนที่จะยกเลิกการโหลดหลังจากผ่านไปห้านาที ข้อดีข้อเสียสามารถดูได้ที่ การคงโมเดล Ollama ไว้ในหน่วยความจำ หากคำศัพท์ของ systemd ที่ใช้ในที่นี้เป็นเรื่องใหม่ วิธีการทำงานของ systemd services และ timers บน VPS จะอธิบายเกี่ยวกับตัว unit เหล่านี้ไว้ครับ
เหตุใดไดเรกทอรีโมเดลจึงแจ้งว่า permission denied ภายใต้ SELinux
บน Fedora, RHEL, Rocky และ AlmaLinux นั้น SELinux จะถูกเปิดใช้งาน (enforcing) เป็นค่าเริ่มต้น กระบวนการทำงานของคอนเทนเนอร์จะรันอยู่ในโดเมน container_t ในขณะที่ไดเรกทอรีในโฮมไดเรกทอรีของผู้ใช้จะถูกติดป้ายกำกับเป็น user_home_t นโยบายความปลอดภัยไม่อนุญาตให้ทั้งสองส่วนเข้าถึงกันได้ ส่งผลให้ Ollama ไม่สามารถสร้างโครงสร้างไดเรกทอรีสำหรับโมเดลและคอนเทนเนอร์จะหยุดทำงาน getenforce จะแสดงผล Enforcing บนระบบเหล่านี้ และการปฏิเสธการเข้าถึงจะถูกบันทึกไว้ดังนี้:
sudo ausearch -m avc -ts recentคุณจะพบข้อความที่ระบุชื่อโดเมนและป้ายกำกับเป้าหมาย:
avc: denied { write } for pid=1842 comm="ollama" name="models" dev="vda1" ino=131077 scontext=system_u:system_r:container_t:s0:c214,c827 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=dir permlisted=0การใช้ :Z ที่ท้ายบรรทัด Volume= คือวิธีแก้ไข โดยจะเป็นการเปลี่ยนป้ายกำกับของไดเรกทอรีบนโฮสต์ให้เป็น container_file_t และประทับตราด้วย MCS (multi-category security) ส่วนตัวซึ่งมีเพียงคอนเทนเนอร์นี้เท่านั้นที่ถือครองอยู่ ส่วน :z แบบตัวพิมพ์เล็กจะใช้ป้ายกำกับร่วมกัน ซึ่งเป็นสิ่งที่ควรใช้เมื่อต้องการให้คอนเทนเนอร์สองตัวอ่านไดเรกทอรีเดียวกัน
ข้อควรระวังเกี่ยวกับ :Z คือมันเป็นการทำงานที่ทำลายค่าเดิมและไม่มีการแจ้งเตือน การเปลี่ยนป้ายกำกับจะทำงานแบบ recursive หากคุณชี้ไปที่ /home/ollama ไฟล์ทุกไฟล์ในโฮมไดเรกทอรีนั้นจะถูกเปลี่ยนป้ายกำกับทั้งหมด ซึ่งจะทำให้การเข้าถึงผ่าน SSH key ของผู้ใช้รายนั้นใช้งานไม่ได้ ควรระบุให้ :Z ทำงานกับไดเรกทอรีย่อยที่จัดเตรียมไว้โดยเฉพาะและไม่มีข้อมูลอื่นปะปนอยู่เสมอ สำหรับ named volumes นั้นไม่จำเป็นต้องใช้ตัวเลือกนี้ เนื่องจาก Podman จะติดป้ายกำกับให้อย่างถูกต้องในขณะที่สร้าง หากคุณต้องการทำความเข้าใจในภาพรวม พื้นฐาน SELinux สำหรับเซิร์ฟเวอร์ จะอธิบายเกี่ยวกับบริบท (contexts) และค่าบูลีน (booleans) ไว้ สำหรับ Ubuntu และ Debian ซึ่งใช้ AppArmor แทนนั้น :Z จะไม่มีผลการทำงานใดๆ และการคงค่านี้ไว้ใน unit file ก็ไม่มีผลเสียแต่อย่างใด
ปิดพอร์ต 11434 และเข้าถึง API ผ่าน SSH
การผูก PublishPort=127.0.0.1:11434:11434 เข้ากับ loopback ของโฮสต์ ให้ตรวจสอบดังนี้:
ss -ltnp | grep 11434
curl http://127.0.0.1:11434ผลลัพธ์ของ ss ต้องแสดง 127.0.0.1:11434 หากเป็น 0.0.0.0:11434 หรือ *:11434 หมายความว่าพอร์ตเปิดสู่สาธารณะ และ curl จะต้องตอบกลับเป็น Ollama is running
โปรดระบุฝั่งที่ต้องการผูกให้ชัดเจน ที่อยู่ภายใน PublishPort คือที่อยู่ของโฮสต์ ภายในคอนเทนเนอร์ Ollama ต้องฟังการเชื่อมต่อจากทุกอินเทอร์เฟซตามค่าเริ่มต้นของอิมเมจ การตั้งค่า Environment=OLLAMA_HOST=127.0.0.1 จะเป็นการผูก Ollama เข้ากับ loopback ของคอนเทนเนอร์เอง และ Podman จะส่งต่อทราฟฟิกที่เผยแพร่ไปยังที่อยู่เครือข่ายของคอนเทนเนอร์ ส่งผลให้ทุกคำขอถูกปฏิเสธแม้จะมาจากโฮสต์ก็ตาม
การเปิดพอร์ต 11434 ทิ้งไว้สร้างความเสียหายสองทาง ประการแรก Ollama ไม่มีการตรวจสอบสิทธิ์ ดังนั้นใครก็ตามที่เข้าถึงพอร์ตนี้สามารถแสดงรายการโมเดลผ่าน /api/tags, รันการอนุมานบน CPU และใช้โควตาแบนด์วิดท์ของคุณผ่าน /api/generate, ดาวน์โหลดโมเดลใหม่ลงในดิสก์ของคุณ และลบโมเดลที่คุณมีอยู่ได้ ประการที่สอง การส่งข้อมูลผ่าน HTTP ธรรมดาไปยังพอร์ตระยะไกลจะทำให้ prompt และผลลัพธ์ถูกส่งเป็นข้อความธรรมดา (cleartext) ซึ่งเครื่องทุกเครื่องที่อยู่ระหว่างทางสามารถอ่านข้อมูลได้ ปัญหาทั้งสองประการจะหมดไปหากพอร์ตไม่ถูกเปิดออกสู่ภายนอก
จากเวิร์กสเตชันของคุณ ให้ส่งต่อพอร์ตผ่าน SSH:
ssh -N -L 11434:127.0.0.1:11434 you@vps.example.comขณะนี้ http://127.0.0.1:11434 บนแล็ปท็อปของคุณคือ Ollama ของเซิร์ฟเวอร์ที่อยู่ภายใต้การเข้ารหัสของเซสชัน SSH หากแล็ปท็อปของคุณรัน Ollama อยู่แล้ว การผูกพอร์ตในเครื่องจะล้มเหลวด้วยข้อผิดพลาด bind [127.0.0.1]:11434: Address already in use ให้ใช้ -L 11435:127.0.0.1:11434 แทนแล้วชี้ไคลเอนต์ของคุณไปที่พอร์ต 11435
เมื่อไคลเอนต์ที่เป็นเบราว์เซอร์จำเป็นต้องใช้งาน ให้ใช้ reverse proxy ที่มีการตั้งรหัสผ่านไว้ด้านหน้าแทน บล็อกไซต์ของ Caddy มีความยาวเพียงสี่บรรทัด และ caddy hash-password จะแสดงค่า bcrypt hash ที่ต้องการ:
ollama.example.com {
basic_auth {
you $2a$14$replace_with_the_generated_hash
}
reverse_proxy 127.0.0.1:11434
}Caddy จะขอใบรับรองผ่าน TLS (transport layer security) โดยอัตโนมัติ ทำให้ทราฟฟิกถูกเข้ารหัส ทดสอบไคลเอนต์ของคุณก่อน เนื่องจากเครื่องมือหลายตัวที่สื่อสารกับ Ollama ไม่มีช่องสำหรับใส่ header Authorization และจะล้มเหลวเมื่อเจอกับ basic auth ที่ไม่มีการตั้งค่าที่เหมาะสมด้วย 401 Unauthorized อุโมงค์ SSH ไม่มีปัญหาดังกล่าว จึงเป็นเหตุผลที่แนะนำให้ใช้วิธีนี้เป็นค่าเริ่มต้น
ดึงโมเดลและตรวจสอบเส้นทางทั้งหมด
podman exec -it ollama ollama pull gemma3:4b
curl -s http://127.0.0.1:11434/api/tags
curl -s http://127.0.0.1:11434/api/generate -d '{"model":"gemma3:4b","prompt":"Reply with the single word: ready","stream":false}'
du -sh ~/ollama-data/models/api/tags จะส่งคืน JSON ที่แสดงรายการ gemma3:4b ส่วน /api/generate จะส่งคืนออบเจกต์ JSON ที่มีฟิลด์ response หลังจากหยุดรอชั่วขณะในระหว่างที่โหลดน้ำหนัก (weights) จากดิสก์ โดย du ควรรายงานตัวเลขที่ใกล้เคียงกับขนาดไฟล์ดาวน์โหลดที่ระบุไว้ จากนั้นให้พิสูจน์ส่วนที่เป็นหัวใจสำคัญของคู่มือฉบับนี้:
sudo reboot
# reconnect, then:
sudo -iu ollama
export XDG_RUNTIME_DIR=/run/user/$(id -u)
systemctl --user is-active ollama.serviceactive หมายถึงสถานะที่ยังคงค้างอยู่ ซึ่งแสดงว่าส่วน [Install] และ daemon-reload ได้ทำงานครบถ้วนแล้ว ส่วน inactive หมายถึงหนึ่งในสามส่วนนี้ขาดหายไป
รูปแบบความล้มเหลวและข้อความที่คุณจะพบ
คอนเทนเนอร์หายไปหลังจากรีบูต ให้ตรวจสอบ loginctl show-user ollama --property=Linger ก่อน เพราะหากไม่มี Linger=yes อินสแตนซ์ systemd ของผู้ใช้จะไม่เริ่มทำงานตอนบูตเครื่อง หากเปิดใช้งาน lingering แล้ว แต่ส่วน [Install] หายไปจากไฟล์ .container หรือคุณแก้ไขไฟล์แล้วแต่ไม่ได้รัน systemctl --user daemon-reload
Error: statfs /home/ollama/ollama-data: no such file or directory ต้นทางของ bind mount ต้องมีอยู่จริงก่อนที่คอนเทนเนอร์จะเริ่มทำงาน Podman จะไม่สร้างไดเรกทอรีบนโฮสต์ให้คุณ ให้รัน mkdir -p ~/ollama-data ในฐานะผู้ใช้ ollama
การเริ่มทำงานล้มเหลวที่ 90 วินาที journalctl --user -u ollama.service แสดง Start operation timed out. Terminating. เนื่องจากกระบวนการดึงอิมเมจยังคงทำงานอยู่ ให้ดึงอิมเมจด้วยตนเอง หรือคงค่า TimeoutStartSec=900 ไว้
คอนเทนเนอร์เริ่มทำงานแล้วหยุดทันที podman logs ollama และ sudo ausearch -m avc -ts recent จะบอกคุณว่าปัญหาเกิดจาก SELinux label หรือไม่ หากมี AVC ที่ระบุ container_t และ user_home_t แสดงว่า :Z หายไป
คำขอถูกปฏิเสธจากโฮสต์ curl: (7) Failed to connect to 127.0.0.1 port 11434: Connection refused พร้อมกับบริการ active มักหมายความว่า OLLAMA_HOST ถูกตั้งค่าเป็น loopback address ภายในคอนเทนเนอร์ ให้ลบบรรทัดนั้นออก
การสร้างผลลัพธ์ช้ามาก หรือคอนเทนเนอร์ถูกสั่งหยุดทำงาน หากไม่มี GPU การประมวลผล inference จะทำงานบน CPU ซึ่งโมเดลขนาดใหญ่จะทำงานช้าโดยธรรมชาติ หากคอนเทนเนอร์หยุดทำงานระหว่างประมวลผลคำขอและมี signal: killed ใน log แสดงว่า kernel ได้สั่งหยุดการทำงานเนื่องจากหน่วยความจำไม่เพียงพอ (OOM killer) ให้เลือก tag ที่มีขนาดเล็กลงจากตารางด้านบน
การอัปเดตอิมเมจที่ถูกตรึงเวอร์ชันไว้
การตรึงเวอร์ชัน (pinning) หมายความว่าการอัปเดตเป็นสิ่งที่คุณต้องดำเนินการเอง ไม่ใช่สิ่งที่เกิดขึ้นโดยอัตโนมัติ ให้แก้ไข Image= ใน ollama.container จากนั้นโหลดการตั้งค่าใหม่และรีสตาร์ทบริการ:
systemctl --user daemon-reload
systemctl --user restart ollama.service
podman exec ollama ollama --versionโมเดลต่างๆ จะอยู่ใน bind mount ดังนั้นข้อมูลจะยังคงอยู่ครบถ้วนแม้จะเปลี่ยนอิมเมจแล้วก็ตาม AutoUpdate=registry ในส่วน [Container] มีไว้สำหรับผู้ที่ใช้งานแท็กแบบเปลี่ยนแปลงได้ (moving tag) ซึ่งจะไม่มีประโยชน์หากใช้คู่กับแท็กเวอร์ชันที่ระบุตายตัว เนื่องจากเนื้อหาของแท็กนั้นจะไม่เปลี่ยนแปลง ให้สำรองข้อมูล /home/ollama/ollama-data/models/manifests และไฟล์ .container ไว้ และไม่ต้องสำรองข้อมูล blobs เนื่องจากมีขนาดใหญ่และ ollama pull จะดาวน์โหลดข้อมูลเหล่านี้ใหม่อีกครั้งเมื่อติดตั้งบนเครื่องใหม่
FAQ
ทำไมคอนเทนเนอร์ Podman แบบ rootless ของฉันถึงหยุดทำงานเมื่อฉันออกจากระบบ?
อินสแตนซ์ systemd ของผู้ใช้และไดเรกทอรี /run/user/<uid> จะถูกยกเลิกเมื่อเซสชันสุดท้ายของผู้ใช้สิ้นสุดลง ส่งผลให้คอนเทนเนอร์แบบ rootless ทั้งหมดหยุดทำงานตามไปด้วย ให้รันคำสั่ง sudo loginctl enable-linger ollama และตรวจสอบว่า loginctl show-user ollama --property=Linger แสดงผลเป็น Linger=yes เปิดใช้งาน lingering ก่อนที่คุณจะสร้าง Quadlet unit เนื่องจากไดเรกทอรีรันไทม์ที่ unit จำเป็นต้องใช้จะมีอยู่ก็ต่อเมื่อเปิดใช้งาน lingering แล้วเท่านั้น
ฉันจำเป็นต้องใส่ SELinux label บนไดเรกทอรีโมเดลของ Ollama หรือไม่?
บน Fedora, RHEL, Rocky และ AlmaLinux จำเป็นต้องทำหากคุณใช้การ bind mount ไดเรกทอรีจากโฮสต์ คอนเทนเนอร์จะทำงานภายใต้โดเมน container_t ในขณะที่ไดเรกทอรีในโฮมโฟลเดอร์จะมี label เป็น user_home_t ทำให้การเขียนข้อมูลถูกปฏิเสธและ Ollama จะหยุดทำงาน ให้เพิ่ม :Z ต่อท้ายบรรทัด Volume= และกำหนดไดเรกทอรีย่อยเฉพาะสำหรับงานนี้ เนื่องจากการเปลี่ยน label แบบ recursive หากชี้ :Z ไปยังไดเรกทอรีโฮมทั้งหมดจะทำให้การเข้าถึงด้วย SSH key ของผู้ใช้นั้นใช้งานไม่ได้ สำหรับ Named volumes นั้น Podman จะจัดการ label ให้ถูกต้องโดยอัตโนมัติและไม่จำเป็นต้องตั้งค่าเพิ่มเติม
โมเดลของ Ollama ต้องการพื้นที่ดิสก์เท่าใด?
ให้เริ่มพิจารณาจากขนาดไฟล์ดาวน์โหลดที่ระบุไว้บน ollama.com/library ซึ่งมีตั้งแต่ 3.3 GB สำหรับ gemma3:4b ไปจนถึง 19 GB สำหรับ qwen3:30b จากนั้นให้บวกขนาดของ Podman image เพิ่มเข้าไป และเผื่อพื้นที่ว่างไว้ด้วยเนื่องจากการดาวน์โหลดโมเดลตัวที่สองจะไม่เข้าไปแทนที่โมเดลตัวแรกบนดิสก์ ให้ตรวจสอบพื้นที่ด้วย df -h /home ก่อนทำการ pull และ du -sh ~/ollama-data/models หลังจากนั้น สำหรับหน่วยความจำ RAM ให้วางแผนในลักษณะเดียวกัน โดยโมเดลต้องการพื้นที่ในหน่วยความจำใกล้เคียงกับขนาดไฟล์เมื่อถูกโหลดขึ้นมาใช้งาน บวกกับพื้นที่สำหรับ context window
การเปิดพอร์ต 11434 บน VPS ปลอดภัยหรือไม่?
ไม่ปลอดภัย Ollama ไม่มีระบบยืนยันตัวตนใดๆ ดังนั้นใครก็ตามที่เข้าถึงพอร์ตนี้ได้จะสามารถดูรายการโมเดลของคุณ ลบโมเดล ดาวน์โหลดโมเดลใหม่ลงในดิสก์ของคุณ รวมถึงรัน inference โดยใช้ CPU และโควตาแบนด์วิดท์ของคุณ นอกจากนี้การสื่อสารผ่าน HTTP ปกติบนอินเทอร์เน็ตยังทำให้ prompt และผลลัพธ์ทั้งหมดถูกส่งเป็นข้อความธรรมดา (cleartext) ให้ผูกพอร์ตฝั่งโฮสต์ไว้ที่ 127.0.0.1 ด้วย PublishPort=127.0.0.1:11434:11434 ตรวจสอบความถูกต้องด้วย ss -ltnp | grep 11434 และเข้าถึงผ่าน SSH tunnel หรือ reverse proxy ที่มีการป้องกันด้วยรหัสผ่านเท่านั้น