วิธีติดตั้ง LinkBreeze บน VPS ด้วย Docker และ Caddy
เรียนรู้วิธีติดตั้ง LinkBreeze เพื่อทำหน้าลิงก์รวมแบบโฮสต์เองบน VPS ด้วย Docker Compose และ Caddy พร้อมเทคนิคจัดการไฟล์ SQLite และการตั้งค่าติดตามคลิกแบบไม่ใช้คุกกี้
LinkBreeze คืออะไร
LinkBreeze เป็นทางเลือกสำหรับทำ Linktree ที่คุณสามารถโฮสต์เองได้ โดยเป็น Docker container เพียงหนึ่งเดียวที่ทำหน้าที่ให้บริการหน้าลิงก์รวม (link-in-bio) สำหรับสาธารณะและแดชบอร์ดสำหรับผู้ดูแลระบบ ข้อมูลสถานะทั้งหมดจะถูกเก็บไว้ในไฟล์ SQLite เพียงไฟล์เดียว ซอฟต์แวร์นี้ใช้สัญญาอนุญาตแบบ MIT เขียนด้วย TypeScript บน Next.js และเผยแพร่ในรูปแบบ ghcr.io/manak-hash/linkbreeze ในการใช้งาน คุณจำเป็นต้องมี VPS, โดเมนที่มี A record ชี้ไปยัง VPS นั้น, เปิดพอร์ต 80 และ 443 รวมถึงติดตั้ง Docker Engine พร้อมปลั๊กอิน Compose
คู่มือนี้ครอบคลุมวิธีการติดตั้งที่ repository รองรับจริง นั่นคือการใช้ Docker Compose ทำงานอยู่เบื้องหลัง reverse proxy ที่จัดการดึงใบรับรอง TLS ด้วยตนเอง นอกจากนี้ยังครอบคลุมถึงสาเหตุที่ทำให้ระบบขัดข้อง เนื่องจากหน้าลิงก์รวมเป็น URL สาธารณะที่ผู้อื่นต้องคลิกเข้ามา หากระบบใช้งานไม่ได้ คุณจะสูญเสียโอกาสในการเข้าถึงจากผู้ใช้งานเหล่านั้น
ก่อนจะเริ่มดำเนินการใดๆ โปรดทำความเข้าใจให้ชัดเจนว่าโครงการนี้ยังถือว่าใหม่มาก
LinkBreeze มีความพร้อมเพียงพอสำหรับใช้เป็นลิงก์โปรไฟล์สาธารณะหรือไม่
ณ เดือนสิงหาคม 2026 repository นี้มี 178 stars, 17 forks และมีผู้ดูแลเพียงคนเดียว โดย release แรกที่มีการ tag คือ v1.0.0 ลงวันที่ 1 กรกฎาคม 2026 ซึ่งหมายความว่าโปรเจกต์นี้มีอายุเพียงไม่กี่สัปดาห์ ไม่ใช่ไม่กี่ปี
The data behind this chart
[
{
"week": "2026-06-29",
"releases": 3,
"cumulative": 3
},
{
"week": "2026-07-06",
"releases": 3,
"cumulative": 6
},
{
"week": "2026-07-13",
"releases": 1,
"cumulative": 7
},
{
"week": "2026-07-20",
"releases": 2,
"cumulative": 9
},
{
"week": "2026-07-27",
"releases": 3,
"cumulative": 12
},
{
"week": "2026-08-03",
"releases": 2,
"cumulative": 14
},
{
"week": "2026-08-10",
"releases": 3,
"cumulative": 17
}
]นับตั้งแต่ v1.0.0 โปรเจกต์ได้ปล่อย 17 tagged releases ในช่วงระยะเวลา 7 สัปดาห์ สัปดาห์สุดท้ายในแผนภูมิดังกล่าวยังคงดำเนินอยู่ในขณะที่เขียนคู่มือนี้ และมีจำนวน release ไปแล้วถึง 3 รายการ
โปรดอ่านข้อมูลข้างต้นเป็นสองประเด็นแยกกัน ประเด็นแรกคือผู้ดูแลมีความกระตือรือร้นและบั๊กได้รับการแก้ไขภายในไม่กี่วัน ประเด็นที่สองคือ schema และค่าเริ่มต้นต่างๆ ยังคงมีการเปลี่ยนแปลงอยู่ ดังนั้น instance ที่คุณติดตั้งแล้วปล่อยทิ้งไว้จะมีความแตกต่างจากโค้ดที่กำลังพัฒนาไปไกลมาก
สัญญาอนุญาต (license) ช่วยคุ้มครองคุณจากกรณีที่เลวร้ายที่สุด โดยการใช้ MIT ร่วมกับ container image และไฟล์ SQLite บนดิสก์ของคุณเอง หมายความว่าหากการพัฒนาหยุดชะงัก สิ่งที่คุณมีอยู่ก็จะยังคงทำงานต่อไปได้ สิ่งที่สัญญาอนุญาตไม่สามารถคุ้มครองคุณได้คือเว็บแอปที่เปิดสู่สาธารณะซึ่งหยุดได้รับการแก้ไขด้านความปลอดภัย ซึ่งจะกลายเป็นภาระความรับผิดชอบเมื่อเวลาผ่านไป ให้ติดตั้งโปรเจกต์นี้ในฐานะสิ่งที่คุณจะคอยอัปเดตอยู่เสมอ และตรวจสอบให้แน่ใจว่าขั้นตอนการสำรองข้อมูลด้านล่างนี้ใช้งานได้ตั้งแต่วันแรก
ตรึงแท็กของอิมเมจไว้ และห้ามใช้ latest
เวิร์กโฟลว์การปล่อยซอฟต์แวร์จะพุชแท็กออกมาสองแท็กต่อหนึ่งเวอร์ชัน ได้แก่ latest และหมายเลขเวอร์ชันที่ตัด v ออก ดังนั้นแท็กที่ถูกตรึงไว้สำหรับรุ่น v1.2.7 จึงเป็น ghcr.io/manak-hash/linkbreeze:1.2.7 การเขียน :v1.2.7 จะไม่ดึงข้อมูลใดๆ และ Docker จะรายงานว่า manifest unknown เนื่องจากแท็กดังกล่าวไม่เคยถูกพุชขึ้นไป
คุณควรตรึงแท็กไว้เพราะ latest มีการเปลี่ยนแปลงอยู่ตลอด ตามรอบเวลาในตารางด้านบน การใช้ docker compose pull กับ latest ถือเป็นการอัปเกรดโดยไม่มีการตรวจสอบสำหรับหน้าเว็บที่ผู้ใช้งานของคุณกำลังเข้าถึงอยู่ แต่หากใช้แท็กที่ตรึงไว้ การอัปเกรดจะเกิดขึ้นก็ต่อเมื่อคุณแก้ไขไฟล์ด้วยตนเองเท่านั้น
อีกเรื่องหนึ่งเกี่ยวกับอิมเมจ เวิร์กโฟลว์การปล่อยซอฟต์แวร์จะสร้างอิมเมจโดยไม่มีการตั้งค่า platforms: ดังนั้นอิมเมจที่เผยแพร่ออกมาจึงเป็น linux/amd64 เท่านั้น บนโฮสต์ที่เป็น arm64 การดึงอิมเมจจะล้มเหลวด้วยข้อผิดพลาด no matching manifest for linux/arm64/v8 in the manifest list entries หากคุณรัน บน ARM VPS แทนที่จะเป็น x86 ให้สร้างอิมเมจบนเครื่องนั้นโดยตรงแทน:
git clone --branch v1.2.7 --depth 1 https://github.com/Manak-hash/LinkBreeze.git
cd LinkBreeze
docker build -t linkbreeze:1.2.7 .จากนั้นให้ใช้ linkbreeze:1.2.7 เป็นชื่ออิมเมจในไฟล์ compose ด้านล่างนี้
การติดตั้ง LinkBreeze หลัง Caddy พร้อม TLS อัตโนมัติ
Caddy จะร้องขอและต่ออายุใบรับรองจาก Let's Encrypt โดยอัตโนมัติ ดังนั้น TLS (transport layer security) จึงไม่จำเป็นต้องมีขั้นตอนการจัดการใบรับรองแยกต่างหาก การติดตั้งทั้งหมดประกอบด้วยไฟล์ 3 ไฟล์ในไดเรกทอรีเดียว
สร้าง secret ก่อนเป็นอันดับแรก:
mkdir -p ~/linkbreeze && cd ~/linkbreeze
printf 'SECRET_KEY=%s\n' "$(openssl rand -hex 32)" > .env
chmod 600 .envSECRET_KEY ใช้สำหรับลงชื่อในคุกกี้เซสชันของผู้ดูแลระบบและทำหน้าที่เป็น salt สำหรับแฮชของผู้เข้าชมในส่วน analytics ไฟล์ compose ที่เผยแพร่ใน repository กำหนดค่าเริ่มต้นไว้ที่ ${SECRET_KEY:-changeme-in-production} ดังนั้นหากคุณข้ามขั้นตอนนี้ อินสแตนซ์ของคุณจะทำงานด้วยคีย์การลงชื่อเซสชันที่ถูกเปิดเผยต่อสาธารณะบน GitHub ให้ตั้งค่านี้ก่อนการเริ่มทำงานครั้งแรก เพราะการเปลี่ยนภายหลังจะทำให้คุณถูกออกจากระบบและรีเซ็ตค่า salt ของ analytics
เขียนไฟล์ docker-compose.yml:
services:
linkbreeze:
image: ghcr.io/manak-hash/linkbreeze:1.2.7
restart: unless-stopped
volumes:
- linkbreeze-data:/app/data
environment:
- DATABASE_PATH=/app/data/linkbreeze.db
- SECRET_KEY=${SECRET_KEY}
- BASE_URL=https://links.example.com
networks:
- linkbreeze-net
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
networks:
- linkbreeze-net
networks:
linkbreeze-net:
volumes:
linkbreeze-data:
caddy-data:
caddy-config:BASE_URL เป็นค่าทางเลือกที่ควรตั้งค่าไว้: มันจะแจ้งให้แอปทราบถึงที่อยู่สาธารณะที่แท้จริง เพื่อป้องกันไม่ให้คำขอที่มาพร้อมกับ header Host ปลอมแปลง ทำให้แอปสร้างลิงก์ไปยังโดเมนของผู้อื่น
เขียนไฟล์ Caddyfile ไว้ข้างๆ กัน โดยใช้โดเมนของคุณเอง:
links.example.com {
encode zstd gzip
reverse_proxy linkbreeze:3000
}โดยค่าเริ่มต้น Caddy จะตั้งค่า X-Forwarded-For และ X-Forwarded-Proto ในคำขอที่ผ่าน proxy ซึ่ง analytics จำเป็นต้องใช้ค่าเหล่านี้ เริ่มการทำงานด้วยคำสั่ง:
docker compose up -d
docker compose ps
docker compose logs -f caddydocker compose ps ควรแสดงคอนเทนเนอร์ LinkBreeze ในสถานะ healthy อิมเมจนี้มาพร้อมกับ healthcheck ในตัวคือ wget --spider -q http://127.0.0.1:3000/api/health ดังนั้นคุณไม่จำเป็นต้องเพิ่มเอง ห้ามคัดลอก healthcheck จากตัวอย่าง Caddy ใน repository มาใช้ เพราะมันเรียกใช้ curl แต่อิมเมจนี้สร้างบน node:22-alpine ซึ่งมีเพียง busybox wget และไม่มี curl คอนเทนเนอร์นั้นจะรายงานสถานะ unhealthy ทั้งที่ยังให้บริการหน้าเว็บได้ตามปกติ
เปิด https://links.example.com ในเบราว์เซอร์ การเข้าชมครั้งแรกจะนำคุณไปยังวิซาร์ดการตั้งค่าที่ /setup เพื่อสร้างบัญชีผู้ดูแลระบบเพียงบัญชีเดียว หลังจากนั้นแดชบอร์ดจะอยู่ที่ /dashboard และแบบฟอร์มเข้าสู่ระบบอยู่ที่ /login บัญชีดังกล่าวเป็นบัญชีภายในของอินสแตนซ์นี้และไม่มีระบบ single sign-on ในตัว ดังนั้นหากคุณต้องการให้แดชบอร์ดใช้การเข้าสู่ระบบร่วมกับบริการอื่นที่คุณโฮสต์อยู่ คุณต้องใช้ forward auth proxy มาวางไว้ด้านหน้า เช่น Authentik ที่โฮสต์เอง
สังเกตสิ่งที่ไฟล์ compose ไม่ได้ทำ: มันไม่ได้เปิดพอร์ต 3000 ออกสู่ภายนอก มีเพียง Caddy เท่านั้นที่ฟังคำขอผ่านอินเทอร์เฟซสาธารณะ หากคุณยังใหม่กับไวยากรณ์ของไฟล์ Compose บทความ พื้นฐาน Docker Compose สำหรับ VPS จะครอบคลุมเนื้อหาที่ไฟล์นี้อ้างอิงถึง และหากคุณมีบริการอื่นทำงานอยู่ด้านหน้าอยู่แล้ว การเปรียบเทียบ Nginx, Caddy และ Traefik จะอธิบายถึงสิ่งที่ต้องเปลี่ยนแปลง ทั้งนี้ repository ได้จัดเตรียมตัวอย่างที่ใช้งานได้จริงสำหรับ Nginx ร่วมกับ Certbot, Traefik และ Cloudflare tunnel ไว้ให้แล้ว
ตำแหน่งที่ข้อมูลของคุณอยู่ และสิ่งที่การสำรองข้อมูลต้องมี
DATABASE_PATH ชี้ไปที่ /app/data/linkbreeze.db ส่วนรูปโปรไฟล์ที่อัปโหลดและรูปตัวอย่างของลิงก์จะถูกเขียนไว้ข้างๆ กันใน /app/data/uploads ข้อมูลทั้งสองส่วนนี้อยู่ใน named volume ที่ชื่อ linkbreeze-data ดังนั้นหน่วยของการสำรองข้อมูลจึงเป็นตัว volume ไม่ใช่แค่ไฟล์ฐานข้อมูลเพียงอย่างเดียว หากคุณกู้คืนเฉพาะไฟล์โดยไม่มีไดเรกทอรีที่เก็บไฟล์อัปโหลด รูปภาพทั้งหมดบนหน้าเว็บจะแสดงผลเป็น 404
ข้อมูลส่วนที่เหลือทั้งหมดจะอยู่ในฐานข้อมูลนั้นเพียงไฟล์เดียว ได้แก่ หน้าเว็บ, ลิงก์, การตั้งค่า, ธีม, รายชื่อผู้รับอีเมล และแถวข้อมูลสถิติ
ให้ทำการคัดลอกในขณะที่ container หยุดทำงาน:
docker compose stop linkbreeze
docker compose cp linkbreeze:/app/data ./backup-$(date +%F)
docker compose start linkbreezeต้องหยุดการทำงานก่อนเสมอ เพราะการคัดลอกฐานข้อมูล SQLite ในขณะที่มีกระบวนการเขียนข้อมูลลงไป อาจทำให้ได้ไฟล์ที่บันทึกธุรกรรมไม่สมบูรณ์ และไฟล์ที่คัดลอกมาจะเปิดไม่ได้เนื่องจากเสียหาย หน้าเว็บจะออฟไลน์ในระหว่างที่ทำการคัดลอก การกู้คืนข้อมูลคือการทำขั้นตอนย้อนกลับ:
docker compose stop linkbreeze
docker compose cp ./backup-2026-08-14/. linkbreeze:/app/data
docker compose start linkbreeze
docker compose logs -f linkbreezeแดชบอร์ดมีฟังก์ชันส่งออกข้อมูลเป็น JSON ซึ่งให้บริการจาก /api/backup ในชื่อ linkbreeze-backup-YYYY-MM-DD.json โดยไฟล์นี้จะประกอบด้วยโปรไฟล์, ลิงก์, การตั้งค่า และธีมที่บันทึกไว้ แต่จะไม่มีประวัติสถิติ, รายชื่อผู้รับอีเมล หรือรูปภาพที่อัปโหลด และการกู้คืนด้วยวิธีนี้จะลบแถวข้อมูลปัจจุบันในตารางทั้งสี่นั้นทิ้งก่อนที่จะนำข้อมูลจากไฟล์เข้าไปแทนที่ ให้ถือว่าวิธีนี้เป็นเพียงการบันทึก snapshot ของการตั้งค่าเพื่อย้ายโฮสต์หรือแก้ไขข้อผิดพลาดจากการแก้ไขข้อมูลเท่านั้น การคัดลอก volume คือการสำรองข้อมูลที่แท้จริง
กฎการจัดเก็บข้อมูลสองข้อที่ใช้ที่นี่เหมือนกับที่อื่นๆ คือเมื่อคุณ ใช้งาน SQLite ในสภาพแวดล้อม production บน VPS ให้เก็บฐานข้อมูลไว้บนดิสก์ภายในเครื่อง เพราะการล็อกไฟล์ของ SQLite ทำงานได้ไม่เสถียรบนระบบไฟล์เครือข่าย และคุณจะพบปัญหาเมื่อหน้าข้อมูลเสียหาย และหากคุณเปลี่ยนจาก named volume มาใช้ host bind mount ให้ทำการ chown ไดเรกทอรีบนโฮสต์ก่อนเสมอ เนื่องจาก container ทำงานด้วยผู้ใช้ที่ไม่ใช่ root คือ node ซึ่งมี uid 1000 ใน node:22-alpine ดังนั้นไดเรกทอรีที่สร้างโดย root จะทำให้ผู้ใช้ดังกล่าวเขียนข้อมูลไม่ได้ ส่งผลให้แอปพลิเคชันเปิดฐานข้อมูลไม่ได้และ container จะหยุดทำงานทันทีที่เริ่มระบบ การใช้ Bind mounts เทียบกับ named volumes ใน Compose ได้อธิบายข้อดีข้อเสียในเรื่องนี้ไว้อย่างละเอียดแล้ว
การวิเคราะห์ข้อมูลและแบนเนอร์ขอความยินยอมที่คุณไม่จำเป็นต้องมี
นี่คือฟีเจอร์ที่ทำให้การโฮสต์หน้าเว็บด้วยตนเองคุ้มค่ากว่าการใช้บริการฟรีจากที่อื่น
การวิเคราะห์ข้อมูลนี้ไม่มีการใช้คุกกี้ ไม่มีการตั้งค่าคุกกี้สำหรับผู้เข้าชม และไม่มีสคริปต์จากบุคคลที่สามโหลดบนหน้าสาธารณะ ผู้เข้าชมจะถูกระบุตัวตนด้วยค่า SHA-256 hash ของที่อยู่ IP, สตริง user agent และ salt โดยจะถูกตัดให้เหลือเพียง 16 ตัวอักษรเลขฐานสิบหก ตัว salt เองนั้นเป็นค่า hash ของวันที่ UTC ปัจจุบันและ SECRET_KEY ของคุณ ดังนั้นค่านี้จะเปลี่ยนไปเมื่อถึงเวลาเที่ยงคืน UTC และไม่สามารถนำค่า hash ของเมื่อวานมาเทียบกับของวันนี้ได้ ที่อยู่ IP ดั้งเดิมจะไม่ถูกบันทึกลงในฐานข้อมูล
การคลิกจะถูกนับที่ฝั่งเซิร์ฟเวอร์ ลิงก์ http ทุกรายการบนหน้าสาธารณะจะชี้ไปยัง /go/<id> บนโดเมนของคุณเอง ซึ่งจะบันทึกการคลิกแล้วตอบกลับด้วยการ redirect แบบ 302 ไปยังปลายทางที่แท้จริง ดังนั้นการนับจำนวนจึงทำงานได้แม้ในกรณีที่ผู้อ่านปิดการใช้งาน JavaScript หรือใช้งานผ่านเบราว์เซอร์ในแอปที่บล็อกคำขอเบื้องหลัง ส่วนการเข้าชมหน้าเว็บจะถูกบันทึกผ่าน /api/track
มีข้อยกเว้นสองประการที่ควรทราบ คำขอที่มี session ของผู้ดูแลระบบที่ถูกต้องจะถูกข้ามไป เพื่อไม่ให้การแก้ไขหน้าเว็บของคุณเองไปเพิ่มตัวเลขสถิติ นอกจากนี้ user agent ที่เป็น crawler ที่รู้จักก็จะถูกข้ามไปด้วยเช่นกัน
ในส่วนของความยินยอม: ไม่มีการจัดเก็บข้อมูลใดๆ ไว้บนอุปกรณ์ของผู้อ่าน และคุกกี้ที่จัดเก็บไว้บนอุปกรณ์ของผู้อ่านคือสิ่งที่แบนเนอร์คุกกี้จำเป็นต้องขออนุญาต อย่างไรก็ตาม ภาระหน้าที่ของคุณยังคงขึ้นอยู่กับสถานที่ที่ผู้อ่านของคุณอาศัยอยู่ ดังนั้นควรตรวจสอบกฎระเบียบที่เกี่ยวข้อง แต่ในที่นี้ไม่มีคุกกี้ติดตามผลให้ต้องเปิดเผย และไม่มีบุคคลที่สามได้รับข้อมูลดังกล่าว
ข้อควรระวังประการหนึ่งที่มักทำให้ผู้ใช้ประหลาดใจคือ หากคุณหมุนเวียน SECRET_KEY ค่า salt รายวันจะเปลี่ยนตามไปด้วย ส่งผลให้ผู้เข้าชมที่กลับมาใช้งานใหม่ทุกคนจะถูกนับเป็นผู้เข้าชมใหม่นับจากช่วงเวลานั้นเป็นต้นไป
เหตุใดคอลัมน์ประเทศในส่วนการวิเคราะห์จึงว่างเปล่า
เนื่องจากไม่มีส่วนประกอบใดใน stack ของคุณที่ตั้งค่า header ของประเทศไว้ LinkBreeze จะระบุประเทศจาก proxy header เช่น cf-ipcountry และ x-vercel-ip-country ในกรณีที่คุณใช้ VPS ที่อยู่หลัง Caddy หรือ Nginx ของคุณเอง header เหล่านี้จะไม่มีอยู่จริง ส่งผลให้ข้อมูลประเทศถูกบันทึกเป็น null และการจำแนกข้อมูลจึงว่างเปล่า ทั้งนี้ภายใน container ไม่มีฐานข้อมูล GeoIP ติดตั้งมาให้
คุณสามารถแก้ไขได้ 2 วิธี วิธีแรกคือการใช้ Cloudflare วางไว้หน้าโดเมน ซึ่งจะเพิ่ม cf-ipcountry ให้กับทุกคำขอที่ผ่าน proxy หรือวิธีที่สองคือการตั้งค่า header เหล่านี้ใน reverse proxy ของคุณเองโดยใช้การค้นหาจาก GeoIP ในเครื่อง
มีข้อควรระวังที่สำคัญกว่านั้นซึ่งคุณควรตรวจสอบ ตัวจัดการการคลิกและการดูจะอ่านที่อยู่ client จาก X-Forwarded-For ก่อน ตามด้วย X-Real-IP และจะใช้ 0.0.0.0 เป็นค่าสำรองหากไม่พบ header ทั้งสอง หากคุณเปิดพอร์ต 3000 ออกสู่สาธารณะโดยตรงโดยไม่มี proxy อยู่ด้านหน้า ผู้เข้าชมทุกคนจะมีค่า hash เดียวกัน ซึ่งจะทำให้จำนวนผู้เข้าชมที่ไม่ซ้ำกัน (unique visitors) แสดงค่าเป็น 1 ตลอดไป และการจำกัดอัตราการเข้าถึง (rate limit) ที่ 60 เหตุการณ์ต่อนาทีต่อ IP จะถูกนำไปใช้กับผู้เข้าชมทั้งหมดของคุณพร้อมกัน หากคุณใช้งานภายใต้ directive reverse_proxy ที่ระบุไว้ข้างต้น Caddy จะตั้งค่า header ดังกล่าวให้คุณโดยอัตโนมัติและปัญหาทั้งสองประการจะหมดไป
การนำเข้าข้อมูลจาก Linktree และสิ่งที่ระบบไม่สามารถดึงมาได้
ตัวช่วยย้ายข้อมูล (migration wizard) ในแดชบอร์ดรองรับทั้ง URL โปรไฟล์สาธารณะและไฟล์ที่ส่งออก (export) มา ระบบสามารถจดจำหน้าเพจจาก linktr.ee, bento.me, lnk.bio, tap.link, hopp.bio, beacons.ai, solo.to, linkfly, mssg.me และ LittleLink รวมถึงไฟล์ HTML และ JSON ทั่วไป สำหรับ URL ของ Linktree หรือ Bento ระบบจะอ่านข้อมูล __NEXT_DATA__ JSON ที่ฝังอยู่ในหน้าเพจเหล่านั้น ส่วนหน้าเว็บแบบสแตติก ระบบจะอ่านจากแท็ก anchor
ข้อมูลที่จะถูกนำเข้ามาประกอบด้วย ชื่อเรื่อง (title), URL, คำอธิบาย (description) และรูปภาพของแต่ละลิงก์ รวมถึงสถานะว่าเป็นลิงก์โปรไฟล์โซเชียลหรือไม่ ตลอดจนชื่อที่แสดง (display name), ประวัติส่วนตัว (bio) และรูปประจำตัว (avatar) คุณสามารถเลือกได้ว่าจะเก็บลิงก์ใดไว้บ้างก่อนที่ระบบจะบันทึกลงฐานข้อมูล
ข้อมูลที่จะไม่ถูกนำเข้ามาคือ ประวัติการวิเคราะห์ (analytics history), ธีมและเลย์เอาต์, รายชื่อผู้ติดตามทางอีเมล, วันที่กำหนดเผยแพร่ และข้อมูลใดก็ตามที่แพลตฟอร์มเดิมเก็บไว้หลังการล็อกอิน คุณควรวางแผนที่จะสร้างรูปลักษณ์ใหม่ด้วยตนเอง และยอมรับว่าประวัติการคลิกเดิมจะยังคงอยู่บนบริการเก่าเท่านั้น
ตัวนำเข้าข้อมูลจะดึง URL จากเซิร์ฟเวอร์ของคุณโดยตรงแทนที่จะดึงผ่านเบราว์เซอร์ ดังนั้นระบบจะปฏิเสธที่อยู่ที่ไม่เป็นสาธารณะ ข้อความ Private/local URLs are not allowed หมายความว่าคุณระบุที่อยู่ภายในเครือข่ายของคุณเอง ซึ่งการปฏิเสธนี้เป็นความตั้งใจของระบบ เพราะหากไม่มีการป้องกันนี้ ผู้ที่มีสิทธิ์เข้าถึงแดชบอร์ดอาจใช้เซิร์ฟเวอร์ของคุณเพื่อตรวจสอบเครื่องอื่นที่เข้าถึงได้เฉพาะจากเซิร์ฟเวอร์ของคุณเท่านั้น สำหรับข้อความอื่นที่คุณอาจพบ ได้แก่ Only http and https URLs are allowed, Request timed out และ Response too large
การดึงข้อมูล (scraping) ขึ้นอยู่กับมาร์กอัปของผู้อื่น หากตัวช่วยย้ายข้อมูลไม่พบสิ่งใดในหน้าเพจที่มีลิงก์ชัดเจน แสดงว่าแพลตฟอร์มนั้นได้เปลี่ยนโครงสร้าง HTML ไปแล้วนับตั้งแต่มีการเขียนตัวแยกวิเคราะห์ (parser) ขึ้นมา แนะนำให้เพิ่มลิงก์ด้วยตนเองแทนการรอการแก้ไข หากสิ่งที่คุณต้องการจริงๆ คือลิงก์ย่อที่วัดผลได้แทนที่จะเป็นหน้าโปรไฟล์ บริการย่อ URL ที่โฮสต์เองอย่าง Shlink สามารถทำหน้าที่นั้นได้และทำงานบนเครื่องเดียวกันได้อย่างราบรื่น
การอัปเดต deployment ที่ระบุเวอร์ชันไว้ (pinned deployment)
# edit the image tag in docker-compose.yml, then
docker compose pull
docker compose up -d
docker compose logs -f linkbreezeการทำ schema migration จะทำงานโดยอัตโนมัติเมื่อ container เริ่มทำงาน เนื่องจากไม่มีวิธีการที่ระบุไว้สำหรับการย้อนกลับ (rollback) การ migration ดังนั้นให้สำรองข้อมูล volume ไว้ก่อนเสมอ การอัปเกรดที่ไม่สามารถย้อนกลับได้จะปลอดภัยก็ต่อเมื่อคุณสามารถกู้คืนสถานะเดิมก่อนการอัปเกรดได้เท่านั้น
Dashboard จะแสดงแถบประกาศเมื่อมี release ใหม่ โดยระบบจะตรวจสอบผ่านการดึงไฟล์เวอร์ชันขนาดเล็กจาก GitHub repository ของโปรเจกต์ทุกๆ 24 ชั่วโมง และไม่มีการส่งข้อมูลใดๆ เกี่ยวกับ instance ของคุณออกไป โปรดอ่านบันทึกประจำรุ่น (release notes) ก่อนที่จะเปลี่ยน tag เวอร์ชัน เนื่องจากในขั้นตอนนี้ของโปรเจกต์ การอัปเดตเวอร์ชันย่อย (minor version) อาจมีการเปลี่ยนแปลงค่าเริ่มต้นที่คุณใช้งานอยู่ได้
รูปแบบความล้มเหลวและข้อความที่คุณจะพบ
manifest unknown ขณะดึงข้อมูล (pulling) แท็กถูกเขียนไว้เป็น :v1.2.7 โดยปกติ Registry tags จะไม่มี v ดังนั้นให้ใช้ :1.2.7 แทน
no matching manifest for linux/arm64/v8 in the manifest list entries อิมเมจที่เผยแพร่นั้นรองรับเฉพาะสถาปัตยกรรม amd64 เท่านั้น ให้ทำการ build บนโฮสต์ ARM โดยใช้ซอร์สโค้ดที่ระบุแท็กไว้
คอนเทนเนอร์รายงาน unhealthy ในขณะที่หน้าเว็บโหลดได้ตามปกติ มีการตั้งค่า healthcheck ในไฟล์ compose ของคุณที่เรียกใช้ curl ซึ่งอิมเมจดังกล่าวไม่มีคำสั่งนี้ ให้ลบออกแล้วปล่อยให้ wget healthcheck ของตัวอิมเมจเองทำงานแทน
Caddy แสดงข้อผิดพลาดเกี่ยวกับใบรับรอง หรือไม่แสดงผลใดๆ เลย ให้ตรวจสอบ docker compose logs caddy สาเหตุที่พบบ่อยคือระเบียน A record ยังไม่ได้ชี้มาที่ VPS นี้ หรือพอร์ต 80 ถูกปิดอยู่ที่ไฟร์วอลล์ ซึ่งเป็นการขัดขวางกระบวนการ ACME (automatic certificate management environment) HTTP challenge ที่ Caddy ใช้เพื่อยืนยันว่าตนเป็นผู้ควบคุมโดเมนนั้น
จำนวนผู้เข้าชมที่ไม่ซ้ำกัน (Unique visitors) ค้างอยู่ที่ 1 ไม่มีพร็อกซีตัวใดตั้งค่า X-Forwarded-For ทำให้ผู้เข้าชมทุกคนถูกจัดกลุ่มเป็นค่าแฮชเดียวกันทั้งหมด
คอนเทนเนอร์หยุดทำงานทันทีหลังจากเริ่มระบบ ทั้งที่เมื่อวานยังทำงานได้ปกติ หากคุณเปลี่ยนจากการใช้ named volume มาเป็นการใช้ host bind mount ไดเรกทอรีข้อมูลจะเป็นของ root ในขณะที่แอปพลิเคชันรันด้วย uid 1000 ทำให้ไม่สามารถเปิดไฟล์ฐานข้อมูลได้ ให้ทำการ sudo chown -R 1000:1000 ไดเรกทอรีบนโฮสต์
คำขอติดตามผล (Tracking requests) ตอบกลับด้วย HTTP 429 มีการจำกัดอัตราการเข้าถึงต่อ IP บน /api/track และ /go/<id> เกินขีดจำกัด ผู้เข้าชมยังคงถูกเปลี่ยนเส้นทางไปยังปลายทางได้ตามปกติ เพียงแต่การคลิกนั้นจะไม่ถูกนับรวมในสถิติเท่านั้น
FAQ
LinkBreeze พร้อมสำหรับการใช้งานเป็นลิงก์ในประวัติ (link in bio) แบบสาธารณะแล้วหรือยัง?
โครงการนี้ยังอยู่ในช่วงเริ่มต้น ณ เดือนสิงหาคม 2026 repository มีดาว 178 ดวง, fork 17 ครั้ง และมีผู้ดูแล 1 คน โดย release แรกเผยแพร่เมื่อวันที่ 1 กรกฎาคม 2026 มีการออก release ใหม่เฉลี่ยมากกว่า 2 ครั้งต่อสัปดาห์ ดังนั้นบั๊กจึงได้รับการแก้ไขอย่างรวดเร็วและพฤติกรรมการทำงานของซอฟต์แวร์ก็เปลี่ยนแปลงบ่อยเช่นกัน สัญญาอนุญาตแบบ MIT และการใช้ไฟล์ SQLite ในเครื่องช่วยให้คุณยังคงใช้งานหน้าเว็บได้แม้การพัฒนาจะหยุดลง แต่เว็บแอปพลิเคชันสาธารณะที่ไม่มีการแก้ไขด้านความปลอดภัยจะกลายเป็นความเสี่ยง ดังนั้นให้ถือว่านี่เป็นซอฟต์แวร์ที่คุณต้องคอยอัปเดตอยู่เสมอ ไม่ใช่ติดตั้งเพียงครั้งเดียวแล้วจบไป
ฉันควรใช้ image tag ใดของ LinkBreeze?
ให้ใช้ tag ตามเวอร์ชัน เช่น ghcr.io/manak-hash/linkbreeze:1.2.7 และเปลี่ยนเวอร์ชันอย่างตั้งใจ กระบวนการ release จะ push เฉพาะ latest และเลขเวอร์ชันเท่านั้น ดังนั้น :v1.2.7 ที่มี v จึงไม่มีอยู่จริงและ Docker จะตอบกลับด้วย manifest unknown ตัว image ถูกสร้างมาสำหรับ linux/amd64 เท่านั้น หากคุณใช้ VPS สถาปัตยกรรม arm64 คุณต้อง clone tag นั้นมาและ build เองในเครื่อง
ทำไมข้อมูลแยกตามประเทศใน analytics ของ LinkBreeze ถึงว่างเปล่า?
LinkBreeze อ่านข้อมูลประเทศของผู้เข้าชมจาก proxy header เช่น cf-ipcountry หรือ x-vercel-ip-country และไม่มีฐานข้อมูล GeoIP เป็นของตัวเอง VPS ที่อยู่หลัง Caddy หรือ Nginx ของคุณเองจะไม่ได้ตั้งค่า header เหล่านี้ ข้อมูลประเทศจึงถูกเก็บเป็นค่าว่าง ให้ใช้ Cloudflare ไว้หน้าโดเมน หรือตั้งค่าให้ reverse proxy ของคุณส่ง header เหล่านี้โดยอ้างอิงจากการค้นหา GeoIP ในเครื่อง
ฉันต้องสำรองข้อมูลอะไรบ้างและกู้คืนอย่างไร?
ให้สำรองข้อมูลทั้ง volume ของ linkbreeze-data ไม่ใช่แค่ไฟล์ฐานข้อมูลเพียงอย่างเดียว /app/data/linkbreeze.db เก็บข้อมูลลิงก์, หน้าเว็บ, การตั้งค่า, ผู้ติดตาม และแถวข้อมูล analytics ทั้งหมด ส่วน /app/data/uploads เก็บรูปภาพ avatar และ thumbnail ที่หน้าเว็บอ้างอิงถึง ให้หยุด container, สั่ง docker compose cp linkbreeze:/app/data ./backup-$(date +%F) แล้วจึงเริ่ม container ใหม่อีกครั้ง การกู้คืนทำได้โดยการคัดลอกไดเรกทอรีกลับเข้าไปใน container ที่หยุดทำงานอยู่แล้วเริ่มระบบใหม่ ส่วนการ export เป็น JSON จาก dashboard เป็นเพียงภาพรวมการตั้งค่าโปรไฟล์, ลิงก์, การตั้งค่า และธีมเท่านั้น ซึ่งไม่มีข้อมูล analytics และรูปภาพรวมอยู่ด้วย
การนำเข้าข้อมูลจาก Linktree จะดึงข้อมูล analytics และธีมมาด้วยหรือไม่?
ไม่ การนำเข้าข้อมูลจะดึงเฉพาะชื่อลิงก์, URL, คำอธิบาย และรูปภาพจากโปรไฟล์สาธารณะเดิมของคุณ รวมถึงชื่อที่แสดง, ประวัติ (bio) และรูป avatar เท่านั้น ส่วนประวัติ analytics, ธีม, รายชื่อผู้ติดตามทางอีเมล และวันที่เผยแพร่ที่ตั้งเวลาไว้จะไม่ถูกดึงมาด้วย คุณต้องตั้งค่ารูปลักษณ์ใหม่ในตัวแก้ไขธีมหลังจากการนำเข้า และโปรดเข้าใจว่าประวัติการคลิกของคุณจะยังคงอยู่บนแพลตฟอร์มเดิม