Vercel ให้ใช้ Dockerfile กับภาษาไหนก็ได้แล้ว แถมรวมหลายเฟรมเวิร์กไว้ในโปรเจกต์เดียวเพื่อ deploy พร้อมกัน
Vercel เปิดให้ใช้ Dockerfile กับแอปภาษาอะไรก็ได้ แล้ว deploy ขึ้น Vercel Functions ได้เลย แต่ไม่ได้แปลว่าจะยัดคอนเทนเนอร์ให้รันยาว ๆ ได้ บทความนี้ชวนดูเงื่อนไขจริง และดูว่า Vercel Services เปลี่ยนวิธีวางหลายเฟรมเวิร์กยังไง

Vercel เป็นแพลตฟอร์มยอดนิยมที่นักพัฒนาใช้ deploy เว็บและแอปขึ้นคลาวด์ ตอนนี้เปิดให้ใช้ Dockerfile เพื่อประกอบแอปภาษาอะไรก็ได้ให้กลายเป็นคอนเทนเนอร์ แล้ว deploy ขึ้นไปรันได้เลย ที่ผ่านมาหลายคนมองว่า Vercel คือที่สำหรับ deploy เว็บที่สร้างด้วย Next.js เป็นหลัก ประกาศรอบนี้จึงน่าสนใจ เพราะทำให้ภาพของ Vercel กว้างกว่าเดิมมาก
จริง ๆ แล้ว Vercel ประกาศสองฟีเจอร์พร้อมกันใน changelog ล่าสุด อย่างแรกคือ Vercel Functions บริการรันโค้ดแบบ serverless ของ Vercel รับ Dockerfile หรือ Containerfile มารันได้ตรง ๆ โดยไฟล์เหล่านี้จะ build ออกมาเป็น OCI image ซึ่งเป็น image มาตรฐานเดียวกับที่ Docker ใช้ เราเขียน HTTP server ด้วยภาษาไหนก็ได้ แล้ว Vercel จะ build และ deploy ให้เองทุกครั้งที่ commit ส่วนอย่างที่สองคือ Vercel Services ที่ให้เอาหลายเฟรมเวิร์กกับหลายแบ็กเอนด์มาอยู่ในโปรเจกต์เดียว โดเมนเดียว แล้ว deploy ออกไปพร้อมกันทั้งชุด
แต่มีกับดักที่ต้องเข้าใจก่อน เพราะคำว่า "รัน Dockerfile ได้" ไม่ได้แปลว่ายัดคอนเทนเนอร์อะไรก็ได้เข้าไปรันค้างทั้งวัน เดี๋ยวมาดูกันทีละข้อว่าเงื่อนไขจริงคืออะไร และของใหม่รอบนี้ปลดล็อกอะไรให้คนสายเว็บกับแบ็กเอนด์บ้าง
เขียน server ด้วยภาษาไหนก็ได้ แล้วให้ Vercel จัดการต่อ
หัวใจของฟีเจอร์แรกคือ เราเขียนแอปด้วยภาษาอะไรก็ได้ ขอแค่ห่อเป็นคอนเทนเนอร์ แทนที่จะผูกกับ runtime ที่ Vercel Functions เตรียมไว้ให้ เราเอา Dockerfile ของตัวเองมาบอกเลยว่าจะ build แอปยังไง
เงื่อนไขสำคัญอยู่ที่ชื่อไฟล์ Vercel จะมองหา Dockerfile.vercel หรือ Containerfile.vercel ไม่ใช่ Dockerfile ธรรมดา ข้างในไฟล์นี้ต้อง start HTTP server ที่ฟังอยู่ที่พอร์ตตามตัวแปร $PORT เพราะ Vercel จะส่งพอร์ตเข้ามาให้ตอนรัน
ตัวอย่างฝั่ง Go จะหน้าตาประมาณนี้ คือ build ในสเตจหนึ่งแล้วค่อยย้ายไฟล์ที่ได้ไปรันบน base ที่เล็กลง
FROM golang:1.24-alpine AS build
# build แอปของเราในสเตจนี้
FROM alpine:3.20
# รัน HTTP server ที่ฟังอยู่ที่ $PORTพอ commit เข้าไป Vercel จะทำงานต่อให้เองเป็นชุด คือ build image ขึ้นมา push ไปเก็บที่ Vercel Container Registry หรือ VCR แล้ว deploy ให้อัตโนมัติ เราไม่ต้องตั้ง pipeline เอง และไม่ต้องจัดการ registry เอง
ที่สำคัญคือ ยังได้ความสามารถเดิมของ Vercel Functions ครบ เรายังได้ preview deployment ของทุก branch, logs, routing และ autoscaling เหมือนตอน deploy โปรเจกต์ปกติ ไม่ได้กลายเป็นคอนเทนเนอร์ที่ลอยอยู่นอกระบบ
ไม่ใช่คอนเทนเนอร์ที่รันค้างทั้งวัน

จุดที่คนเข้าใจผิดง่ายที่สุดคือตรงนี้ "รัน Dockerfile ได้" ฟังดูเหมือนเราจะเอาคอนเทนเนอร์อะไรก็ได้มายัดให้รันยาว ๆ ทั้งวันทั้งคืน แต่จริง ๆ ไม่ใช่แบบนั้น
คอนเทนเนอร์ที่เอามารันต้องเปิด HTTP server ฟังที่ $PORT และทำงานเป็นฟังก์ชันที่ตอบทีละ request บน Fluid compute โมเดลรันโค้ดของ Vercel ที่สเกลขึ้นลงตามทราฟฟิก มันคือฟังก์ชันที่ตื่นขึ้นมาตอบ request แล้วจบงาน ไม่ใช่ VM หรือ daemon ที่เปิดทิ้งไว้ตลอดเวลา
พูดอีกแบบคือ Vercel ไม่ได้กลายเป็นบริการเช่าเครื่องไว้รันอะไรก็ได้ตลอด 24 ชั่วโมง มันยังเป็นแพลตฟอร์มที่ทำงานตอบทีละ request เหมือนเดิม แค่เปิดให้เราเอาคอนเทนเนอร์ของตัวเองในภาษาไหนก็ได้มาเป็นตัวตอบ request แทน ถ้าเข้าใจขอบเขตตรงนี้ก่อน ก็จะเลือกถูกว่างานแบบไหนเอามาวางตรงนี้ได้ งานแบบไหนต้องไปหาที่อื่น
รวมหลายเฟรมเวิร์กไว้ในโปรเจกต์เดียว

ฟีเจอร์ที่สองคือ Vercel Services ที่แก้ปัญหาคนละเรื่องกัน ปกติเวลาเรามีหลายส่วน เช่น เว็บหน้าบ้านหนึ่งตัวกับ API หลังบ้านอีกหนึ่งตัว เรามักต้องแยกเป็นคนละโปรเจกต์ คนละโดเมน แล้วมานั่งต่อกันเอง Vercel Services ให้เอาหลาย frontend และหลาย backend มาอยู่ในโปรเจกต์เดียว ใช้โดเมนร่วมกัน
ประกาศในไฟล์ vercel.json ที่คีย์ services โดยแต่ละ service บอกแค่ root ว่าโค้ดอยู่โฟลเดอร์ไหน และ framework ว่าใช้เฟรมเวิร์กอะไร หน้าตาคร่าว ๆ เป็นแบบนี้
{
"services": {
"ชื่อ-service": {
"root": "โฟลเดอร์ของ service",
"framework": "เฟรมเวิร์ก เช่น fastapi, express"
}
}
}จากนั้น Vercel จะ auto-detect เฟรมเวิร์กให้ แล้วจัดการ routing, builds และ environment variables ให้เองโดยไม่ต้องตั้งค่าเยอะ เฟรมเวิร์กที่รองรับครอบคลุมหลายสาย โดยสาย Python มี FastAPI กับ Flask สาย JavaScript มี Express กับ Hono ส่วน Go กับ Rust รองรับเต็มรูปแบบโดยไม่ต้องตั้งค่าเพิ่ม ถ้าเฟรมเวิร์กที่ใช้อยู่ในลิสต์นี้ก็หยิบมาวางได้เลย แต่ถ้าภาษาหรือเฟรมเวิร์กไม่อยู่ในนี้ ก็ย้อนกลับไปใช้ Dockerfile แบบหัวข้อก่อนหน้าได้
จุดที่เปลี่ยนวิธีทำงานจริง ๆ คือ deploy, preview และ rollback ได้ทั้งโปรเจกต์พร้อมกันเป็นชุด ทุก service ขึ้นเวอร์ชันพร้อมกัน ไม่เกิดเหตุการณ์ที่ frontend ขึ้นเวอร์ชันใหม่ไปแล้ว แต่ backend ยังค้างอยู่เวอร์ชันเก่า
deploy ทั้งโปรเจกต์เป็นก้อนเดียว ไม่มีสถานะ deploy ค้างครึ่งทาง
เรื่องเงินก็ตามโมเดล Fluid compute เหมือนกัน คือใช้ Active CPU pricing จ่ายเฉพาะช่วงที่โค้ดรันจริง ไม่ได้จ่ายค่าเครื่องที่เปิดทิ้งไว้เฉย ๆ
ให้ service คุยกันโดยไม่ออกเน็ต
พอหลาย service มาอยู่ด้วยกัน คำถามถัดมาคือจะคุยกันยังไง คำตอบคือ Vercel Services ใช้สิ่งที่เรียกว่า service bindings เพื่อให้ service เรียกหากันแบบ private ไม่ต้องวิ่งออกอินเทอร์เน็ตสาธารณะ
วิธีคิดง่าย ๆ คือ service หนึ่งประกาศ binding ชี้ไปอีก service หนึ่ง แล้ว Vercel จะ inject URL ภายในของปลายทางเข้ามาเป็น environment variable ให้ ตัวอย่างเช่น service my_frontend ประกาศ binding ไปที่ my_backend แล้ว Vercel ก็ inject URL ภายในเข้ามาเป็นตัวแปรชื่อ BACKEND_INTERNAL_URL ฝั่ง frontend เรียก backend ผ่าน process.env.BACKEND_INTERNAL_URL ได้เลย โดยไม่ต้องจำหรือ hardcode โดเมนเอง
ผลพลอยได้ที่น่าสนใจคือด้านความปลอดภัย เพราะ service ที่ไม่ได้ตั้ง rewrites สาธารณะไว้จะเข้าถึงได้ทาง binding เท่านั้น แปลว่าไม่มี public route เลย เหมาะกับ backend ที่อยากให้เรียกได้เฉพาะภายใน ส่วน service ไหนที่อยากเปิดให้โลกภายนอกเข้าถึง ก็ใช้ rewrites เพื่อ route request สาธารณะเข้าไปยัง service ที่ถูกต้อง
ฝั่งคนพัฒนาก็ได้เครื่องมือมาช่วยดูภาพรวมด้วย หน้า Deployments จะแสดง services graph ให้เห็นว่ามีกี่ตัวต่อกันยังไง ส่วน Logs กรองดูเฉพาะ service ที่สนใจได้ และคำสั่ง vercel dev จะรัน service ทุกตัวพร้อมกันบนเครื่องเราเหมือนตอนอยู่ production ช่วยให้เทสต์ระบบหลาย service ได้ก่อนปล่อยจริง
ลองเองวันนี้ เริ่มจากไฟล์เดียว
ของแบบนี้ลองได้เร็ว เพราะจุดเริ่มต้นคือไฟล์เดียวในโปรเจกต์เดิม เลือกทางที่ตรงกับงานก่อน
ถ้าอยากเอาแอปในภาษาที่ Vercel ไม่มี runtime ให้ ก็เริ่มจากทาง Dockerfile
- สร้างไฟล์
Dockerfile.vercelที่ root ของโปรเจกต์ บรรทัดแรกใส่ base image ที่ใช้ เช่นFROM golang:1.24-alpine AS build - เขียนให้คอนเทนเนอร์ start HTTP server ที่ฟังอยู่ที่
$PORT - commit แล้วปล่อยให้ Vercel build, push ไป VCR และ deploy ให้เอง
ถ้าอยากรวมหลายส่วนไว้ที่เดียว ก็เริ่มจากทาง Services
- เพิ่มคีย์
servicesในvercel.jsonระบุrootกับframeworkของแต่ละตัว - ถ้ามี service ที่ต้องคุยกันภายใน ก็เพิ่ม binding ชี้หากัน แล้วเรียกผ่าน environment variable ที่ Vercel inject มาให้
- รัน
vercel devเพื่อทดสอบทุก service พร้อมกันบนเครื่องก่อน แล้วค่อย deploy
รายละเอียดเชิงลึกของฝั่ง Dockerfile อ่านต่อได้ในเอกสาร Container Images ของ Vercel ส่วนใครอยากเข้าใจ Fluid compute และ Active CPU pricing ก่อนคิดเรื่องค่าใช้จ่าย ก็ตามลิงก์ในแต่ละหัวข้อด้านบนไปอ่านได้
สิ่งที่เปลี่ยนไปจริง ๆ
คำถามที่เปลี่ยนไปไม่ใช่ "Vercel รองรับภาษาอะไรบ้าง" อีกต่อไป แต่กลายเป็น "โค้ดของเราตอบ request แล้วจบงานเป็นครั้ง ๆ ได้ไหม" ถ้าใช่ จะเขียนด้วยภาษาไหน เฟรมเวิร์กไหน หรือห่อมาเป็นคอนเทนเนอร์เอง ก็มาอยู่รวมกันในโปรเจกต์เดียวที่ deploy พร้อมกันได้หมด
ที่มา:
- บทความ Bring your Dockerfile to Vercel Functions จาก Vercel
- บทความ Run multiple frameworks in one project with Vercel Services จาก Vercel



