ช่วงที่ผ่านมา ผมมีโอกาสย้ายระบบจาก Supabase Cloud มาเป็นแบบ Self-Hosting คือ ติดตั้งใน Server ของเราเอง ก็เลยอยากแชร์ประสบการณ์ เผื่อเป็นประโยชน์กับคนที่ใช้ Supabase Cloud อยู่แล้ว อนาคตอยากมาติดตั้ง Supabase ไว้ใช้ในองค์กรหรือใช้เองครับ
.
1. ทำไมต้องย้ายออกจาก Supabase Cloud?
เดิมทีผมใช้งาน Supabase Cloud กับเว็บ codingthailand.com อยู่ และเป็นแบบฟรี เนื่องจากมีการใช้งานมากขึ้น เลยต้องขยับไปใช้ Pro plan ราคาจะอยู่ที่ $25 / เดือน และถ้าต้องการ Custom Domain เป็นของเราเองก็ต้องจ่ายเพิ่มอีก $10 ต่อดือนด้วย
เหตุผลที่จำเป็นต้องใช้ Custom Domain เพราะมีผู้เรียนหลายท่านอยู่ที่ประเทศลาว มีปัญหาในการใช้งาน ซึ่งมีการ block domain ที่ Supabase generate ให้ ทำให้ไม่สามารถใช้งานระบบ Auth และ API ได้ จุดนี้กลายเป็น pain point หลักที่ทำให้ต้องพิจารณาทางเลือกอื่น และแน่นอนต้องการประหยัดค่าใช้จ่ายในส่วนนี้ด้วย
ของเดิมผมใช้ Server สำหรับเว็บ codingthailand.com deploy ไปยัง Vercel แบบฟรีครับ ซึ่งก็ไม่พอเหมือนกันทำให้ต้องจ่ายให้กับ Vercel ด้วย (เดือนละ $20!)
เลยตัดสินใจย้ายมาใช้ Server Hetzner (VCPUS 4 RAM 8GB SSD 80GB) ราคา $5.99 ต่อเดือน ก็ดีขึ้นมาหน่อย เอาไว้รัน Next.js (Docker)แล้วเชื่อมไปที่ Supabase Cloud นั่นเอง
สรุป ค่าใช้จ่ายต่อเดือน ถ้าจะใช้ supabase cloud และ vercel ต่อ คือ $25+$10+$5.99+$20=$60.99 ต่อเดือนครับ
ป.ล. อ่านโพสต์เก่าบันทึกการเขียนเว็บไซต์ใหม่ในรอบ 10 ปี ที่นี่ได้ครับ
https://www.facebook.com/share/p/17ZtrXMyTG
.
2. แนวทางการย้ายจาก Supabase Cloud มาใช้ Supabase Self-Hosting
ผมไม่อยากจัดการ Infrastructure เอง ไม่อยากทำ SSL เอง และยังอยากได้การ Deploy แบบ Vercel อยู่ (Zero-Downtime Deployment) ผมเลยตัดสินใจเลือกใช้ Coolify เป็น Open Source) ด้วยครับ
ผมเลือกใช้ Coolify เป็นตัวจัดการ Infrastructure แนวคิดคืออยากได้ประสบการณ์ใกล้เคียงกับ Vercel แต่ราคาถูกกว่า สามารถ Deploy ภาษาอะไรก็ได้ Service อะไรก็ได้ และอนาคตสามารถ self-host ได้เช่นเดียวกัน (ตอนนี้ใช้บริการ Cloud เดือนละ $5 อยู่ครับ) อนาคตถ้ามีเวลาค่อยมาทำ self-host อีกที
เผื่อใครสนใจใช้ Coolify ดูที่นี่ครับ https://coolify.io/
และตอนนี้ผมปรับ Server ของ Hetzner เพิ่มขึ้นมาเป็น (VCPUS 8 RAM 16GB SSD 160GB) เพื่อรองรับ supabase self-host และงานอื่นๆ ของผมเช่น API ที่ใช้สอน เว็บไซต์ Blog เป็นต้น และจ่ายเดือนละ $9.99 ครับ
ดู requirement ของ supabase cloud ที่นี่ อาจไม่ต้องใช้เยอะเหมือนผมก็ได้
https://supabase.com/docs/guides/self-hosting/docker…
.
3. การติดตั้ง Supabase Self-Hosting ด้วยการใช้ Coolify
Coolify มี Resource/Service ของ Supabase อยู่แล้ว ทำให้กดมาติดตั้งได้เลย (ใช้ Docker Compose) แต่แน่นอน Supabase ที่ Coolify เตรียมมาให้ใช้ได้แค่บางส่วนเท่านั้น ทำให้ผมต้องไปศึกษาเพิ่มจาก Docker Compose ของ Supabase เอง ที่นี่
https://github.com/supabase-community/supabase-traefik
(Coolify ใช้ Traefik เป็น Reverse Proxy )
ถ้าลองไปดูไฟล์ที่ชื่อว่า docker-compose.example.yml จะเห็นว่ามี config ตัวอย่างอยู่บ้าง เลยลองเปรียบเทียบกับของ Coolify ดูว่ามีส่วนไหนที่แตกต่างกันบ้าง และต้องไปศึกษา config อะไรเพิ่มไหม ปรากฏว่าเวอร์ชันของ service ต่างๆ ที่ Coolify ให้มาเก่ากว่า ของ Supabase พอสมควร ผมเลยจัดการลองอัปเดตเวอร์ชันดู (ใช้ AI ช่วย) และดูเวอร์ชันล่าสุดประกอบที่ Docker Hub ของ Supabase
https://hub.docker.com/u/supabase
จากนั้นก็ทำการ Deploy เพื่อทดสอบดูในรอบแรกก่อน และลองใช้งานคร่าวๆ ดูครับ
.
4. เริ่มวางแผนย้ายระบบ Authentication ก่อนเลย เรียกสั้นๆ ว่า supabase-auth การย้ายนั้นเราใช้คำสั่ง SQL ปกติครับ ตัวอย่าง
SELECT
id,
email,
encrypted_password,
email_confirmed_at,
raw_app_meta_data,
raw_user_meta_data,
created_at,
updated_at
FROM auth.users
WHERE deleted_at IS NULL
ORDER BY created_at
.
เมื่อได้ผลลัพธ์แล้ว ก็ export เป็น CSV เพื่อเตรียม Import ไปยังตัว Self-Host
เมื่อมาที่ Self-Host ก็ Import ปกติเลยครับ ข้อมูล users ทั้งหมดก็จะมาแล้ว (เห็นเขียนสั้นๆ แต่ทดลองหลายรอบมาก ฮ่าๆ)
.
ทีนี้ฝั่ง Next.js 16.x (เว็บไซต์) ก็ต้องแก้ env ให้เป็นตัวใหม่ด้วยนะครับ ทั้ง SUPABASE_URL (โดเมนของ Supabase ของผมจะเป็น https://db.codingthailand.com) และ SUPABASE_ANON_KEY ส่วนนี้ Coolify จะ generate ไว้ให้แล้วครับ ไป copy มาใส่ได้เลย
จากนั้นลองทดสอบการเข้าสู่ระบบของผู้ใช้ ปรากฏว่า เข้าไม่ได้!
ต้องไปดู log ของ docker container ของ service supabase-auth ว่าเกิดจากอะไร (supabase ใช้ GoTrue) ตามดูที่นี่
https://github.com/netlify/gotrue
แล้วพบว่าค่าที่ Coolify ตั้งมาใช้ไม่ได้ คือ ต้องแก้ Docker Compose ใหม่ ค่านั้น คือ
API_EXTERNAL_URL=https://db.codingthailand.com และ
GOTRUE_SITE_URL=https://codingthailand.com
สังเกตดีๆ คนละตัวกันนะครับ
API_EXTERNAL_URL คือ URL ของ Supabase API จริงๆ
ส่วน GOTRUE_SITE_URL คือ ต้องเป็น domain เว็บไซต์ของหน้าบ้านนั่นเอง
จากนั้นผมทดสอบอีก ปรากฏว่า ล็อกอินสำเร็จครับ!
แต่ ผู้ใช้กดลิงก์ ลืมรหัสผ่าน แล้วไม่มีเมลส่งไป!
ต้องมาตั้งค่า SMTP กันต่อครับ
ระบบส่งเมลผมใช้ของฟรีครับ คือ Resend (https://resend.com/) อยู่ ใน supabase-auth service ของ Docker Compose ก็ตั้งค่า ดังนี้
SMTP_HOST=smtp.resend.com
SMTP_PORT=587
SMTP_USER=resend
SMTP_PASS=your-api-key
SMTP_ADMIN_EMAIL=noreply@codingthailand.com
SMTP_SENDER_NAME=CodingThailand
ที่ Firewall ของ Server อย่าลืม allow port 587 นะครับ เกือบตกม้าตายเหมือนกัน ตอนแรกส่งเมลไม่ได้ ฮ่าๆ
.
ต่อมาอันนี้สำคัญมาก อย่าลืมตั้งค่า ADDITIONAL_REDIRECT_URLS เป็น whitelist ของ URL ที่อนุญาตให้ Supabase Auth redirect ผู้ใช้กลับไปหลังจากทำ authentication เสร็จแล้ว เพื่อความปลอดภัย ตัวอย่างการตั้งค่านี้ ลองปรับดุครับ
ADDITIONAL_REDIRECT_URLS=http://localhost:3000/**,https://codingthailand.com/**,https://app.codingthailand.com/**
มาถึงตอนนี้เราจะล็อกอิน และส่งอีเมลได้ตามปกติแล้วครับ
.
แต่ปัญหาคือ อีเมลที่ส่งไปเป็น template ภาษาอังกฤษ!
.
5. สิ่งที่ทำให้ผมเสียเวลาไปมาก คือส่วนนี้ครับ เป็นเรื่องของ Email Templates แก้ ค่า Template ยังไงก็ยังส่งเป็นภาษาอังกฤษอยู่ดี ไม่ส่งเป็น Template ภาษาไทยตามที่อยากได้ ถ้าใช้ Supabase Cloud เราแค่แก้ไขในหน้าเว็บก็ได้เลย แต่ self-host เราต้องกำหนดเองผ่าน config ของ GoTrue (supabase-auth service) ครับ
คือปกติ ถ้าไม่ config อะไรจะเป็นภาษาอังกฤษแน่ๆครับ เป็น default content ของเค้า
ผมกำหนด Docker Compose แบบนี้ (ทำ docker volume)
MAILER_TEMPLATES_INVITE =/templates/invite.html
MAILER_TEMPLATES_CONFIRMATION=/templates/confirm.html
MAILER_TEMPLATES_RECOVERY=/templates/recover.html
MAILER_TEMPLATES_EMAIL_CHANGE=/templates/email-change.html
ป.ล. content ไฟล์ *.html ต่างๆ สามารถ copy มาจาก Supabase Cloud ได้เลยนะครับ ถ้าใคร custom template email ภาษาไทยบน cloud อยู่แล้ว
.
อยากจะบอกว่าแบบนี้ก็ไม่ได้! เลยต้องไปดู log ของ container อีกครั้ง สรุปคือ ค่า default ที่แจ้งมาเป็น http://localhost:3000/invite.html (ประมาณนี้) อ้าว คือต้องเป็น URL จริงๆ แบบนี้เลย ถ้าเป็น file path ไม่ได้! สงสัยเป็นความผิดของผมเองที่ไม่อ่าน docs ดีๆ ( ถาม AI แล้วก็ไม่รู้เหมือนกัน และบอกผิดด้วย)
.
วิธีแก้ คือ
ถ้าใครเคยใช้ Docker Compose มาก่อน เราจะใช้ localhost ในการอ้างชื่อ service ไมไ่ด้แน่ๆ ผมเลยจำเป็นต้องสร้าง service nginx ขึ้นมา เพื่อ serve ไฟล์ .html ได้อย่างถูกต้อง ด้วยการแก้ Docker Compose ดังนี้
email-template-server:
image: ‘nginx:1.29-alpine’
volumes:
– ‘/root/auth-templates:/usr/share/nginx/html:ro’
networks:
– default
restart: unless-stopped
และตั้งค่าใหม่ ดังนี้
-‘GOTRUE_MAILER_TEMPLATES_CONFIRMATION=http://email-template-server/confirmation.html’
– ‘GOTRUE_MAILER_TEMPLATES_INVITE=http://email-template-server/invite.html’
– ‘GOTRUE_MAILER_TEMPLATES_RECOVERY=http://email-template-server/recovery.html’
– ‘GOTRUE_MAILER_TEMPLATES_MAGIC_LINK=http://email-template-server/magic_link.html’
– ‘GOTRUE_MAILER_TEMPLATES_EMAIL_CHANGE=http://email-template-server/email_change.html’
ข้อสังเกต คือ ผมใช้ชื่อ service ( email-template-server) แทน localhost:3000 นั่นเองครับ และทำ docker volume ตรงนี้ volumes:
– ‘/root/auth-templates:/usr/share/nginx/html:ro’
เสร็จแล้วจัดการ Re-Deploy ใหม่ และสามารถส่ง Email เป็นภาษาไทยได้เรียบร้อยครับ
.
6. ลงมือย้ายตารางต่างๆ ใน Database
มาถึงตรงนี้ก็ไม่น่ายากแล้วครับ ใครที่มีความรู้ฐานข้อมูลก็สบายหน่อย เริ่มด้วยการที่เรา copy table schema ของตารางบน cloud แล้วเอามารันที่ self-host แค่นั้นเลยครับ แล้ว export data เป็น csv แล้วค่อย import เข้าปกติ ง่ายมากๆ
สิ่งที่ควรระวังในการออกแบบ table คือ เรื่องของ primary key ครับ คือพวกคอลัมน์ id ไม่ควรใช้ int ที่รันแบบ indentity อย่างเดียว เพราะเหมือนย้ายมาแล้วเค้าจะกลับมารันใหม่ ทำให้ pk ซ้ำ insert ไม่ได้ แนะนำเปลี่ยนไปใช้ pk แบบ uuid และเลือก default value เป็น gen_random_uuid() น่าจะดีกว่าครับ
และเมื่อย้ายมาแล้วอย่าลืม สร้าง Policies และปิด RSL ด้วยนะครับ
7. สรุป
ข้อดี
– หลังย้ายมา Seft-Host ค่าใช้จ่ายต่อเดือนลดลงอย่างชัดเจน จาก $60.99 เหลือ $14.99 (รวมค่า coolify cloud ด้วย $5/เดือน) อนาคตอาจเหลือแค่ $9.99 ถ้าอยากทำ self-host coolify ต่อครับ
– ควบคุม Domain และ Network ได้ 100%
– ไม่เจอปัญหา block domain ตาม region แล้ว
– ระบบโดยรวม ประสิทธิภาพดีขึ้น
– ไม่ต้องต่อ SSL เอง สามารถ Deploy แบบ Zero-Downtime ได้ ดึงข้อมูลจาก GitLab ได้เลย มีระบบ Backup และ Monitoring ให้เสร็จสรรพด้วยการใช้ Coolify
.
ข้อเสีย
– ต้องดูแล Server และ Security เองทั้งหมด
– Configuration ซับซ้อนกว่า Cloud มาก
– ต้องเรียนรู้วิธีใช้ Coolify และ Traefik เพิ่ม
– Single Point of Failure (อย่าลืมทำ backup เสมอ)
.
ใครกำลังใช้ Supabase Cloud แล้วเริ่มเจอ pain point คล้าย ๆ กัน ลองไปปรับใช้ดูนะครับ