คู่มือ EAS Build, Submit และ Update 2026: Deploy แอป React Native บน iOS และ Android ครบจบ

คู่มือ deploy แอป React Native ปี 2026 ด้วย EAS Build, Submit และ Update ครบไปป์ไลน์ ตั้งแต่ eas.json, build profile, runtime version, EAS Workflows ถึงค่าบริการจริง พร้อมโค้ดและประสบการณ์จากโปรเจกต์ production

EAS Build & Update Guide: React Native 2026

อัปเดตเมื่อ: 31 พฤษภาคม 2026

EAS Build, EAS Submit และ EAS Update คือชุดบริการคลาวด์ของ Expo ที่ใช้ build, ส่งขึ้นสโตร์ และอัปเดต React Native แอปแบบ over-the-air ในไปป์ไลน์เดียวกัน โดยที่นักพัฒนาไม่ต้องดูแลเครื่อง Mac, Xcode หรือ Android Studio บนเซิร์ฟเวอร์ของตัวเอง บทความนี้จะอธิบายวิธี deploy แอป React Native ปี 2026 ตั้งแต่กำหนด build profile, จัดการ credentials, ส่งขึ้น App Store และ Google Play อัตโนมัติ ไปจนถึงการกระจาย OTA update ผ่าน runtime version พร้อมโค้ดจริงที่ผมใช้ในโปรเจกต์ production ของตัวเอง (ที่ปล่อย release ทุกสัปดาห์มาเกือบสองปี)

  • EAS Build ใช้ build profile ใน eas.json เพื่อแยก binary ระหว่าง development, preview และ production โดยใช้คลาวด์ของ Expo
  • EAS Submit รับ build จาก EAS Build โดยอัตโนมัติแล้วส่งขึ้น App Store Connect และ Google Play Console โดยไม่ต้องอัปโหลดไฟล์เอง
  • EAS Update กระจายโค้ด JavaScript และ asset ใหม่ผ่าน OTA โดยอ้างอิงกับ runtime version เพื่อกัน update ไปทับ native build ที่ไม่เข้ากัน
  • EAS Workflows (GA ปี 2026) ทำหน้าที่ CI/CD orchestration เชื่อม build, submit และ update ในไฟล์ YAML เดียว
  • Production tier ของ Expo คิดค่าบริการตาม build minutes ไม่ใช่จำนวนแอป จึงเหมาะกับทีมที่ปล่อย release ถี่
  • การใช้ custom dev client แทน Expo Go จำเป็นเมื่อโปรเจกต์มี native module นอกชุด Expo SDK

EAS คืออะไรและทำไมต้องใช้ปี 2026

Expo Application Services (EAS) คือชุดบริการคลาวด์สำหรับ build, sign, submit และ update แอป React Native ทั้งฝั่ง Expo และ bare workflow ตัว EAS แยกเป็นสามบริการหลักที่เชื่อมกันอย่างแนบแน่น ได้แก่ EAS Build สำหรับสร้าง binary, EAS Submit สำหรับส่งขึ้นสโตร์ และ EAS Update สำหรับการกระจาย JavaScript bundle ใหม่ผ่าน over-the-air

เหตุผลที่ทีมส่วนใหญ่ย้ายมาใช้ EAS ในปี 2026 คือต้นทุน infrastructure ที่ต่ำกว่าการดูแล build farm เอง ผมเคยดูแล Jenkins ที่มี Mac mini สามเครื่องสำหรับ build iOS ค่าเช่ารวมไฟฟ้าและการดูแลรักษาแพงกว่าค่า EAS subscription หลายเท่าตัว ยังไม่นับเวลาที่หมดไปกับการอัปเดต Xcode, จัดการ provisioning profile หรือล้าง derived data เมื่อ build แตก (ซึ่งเกิดบ่อยกว่าที่ใครจะเชื่อ)

ในปี 2026 Expo SDK 53 ขึ้นไปทำงานบน React Native New Architecture (Fabric + TurboModules) เป็นค่าเริ่มต้น EAS Build รองรับ Xcode 16, Android API 35 และ Hermes รุ่นใหม่โดยอัตโนมัติ ทีมจึงไม่ต้องตามอัปเดต toolchain เอง อีกจุดที่เปลี่ยนคือ EAS Workflows ที่เปลี่ยนสถานะจาก preview เป็น GA และมาแทน GitHub Actions หรือ Bitrise สำหรับงาน mobile CI/CD ส่วนใหญ่ ผมเลือก EAS เพราะมัน build บน worker ที่ Expo จัดการเอง ทำให้ Xcode upgrade ไม่ใช่ปัญหาของทีม dev อีกต่อไป

ติดตั้ง EAS CLI และเตรียมโปรเจกต์

เริ่มจากติดตั้ง EAS CLI ใน global หรือ devDependencies ของโปรเจกต์ ผมแนะนำ devDependencies เพื่อ pin เวอร์ชันตาม branch ของโค้ดและกัน drift ระหว่างเครื่องนักพัฒนาทุกคน

npm install --save-dev eas-cli@latest
npx eas --version
# 14.x.x

npx eas login
npx eas init --id <your-expo-project-id>

คำสั่ง eas init จะเพิ่ม extra.eas.projectId ใน app.json ของคุณ ค่านี้ต้อง commit เข้า repo เพราะถูกใช้เป็น identifier เมื่อสั่ง build ห้ามใช้ projectId ของโปรเจกต์อื่นแม้จะเป็นทีมเดียวกัน เพราะ Expo จะนับ build minutes ผูกกับ projectId

ถ้าโปรเจกต์ของคุณเป็น bare workflow (มีโฟลเดอร์ ios/ และ android/ อยู่แล้ว) ให้รัน npx expo install expo เพิ่มเข้าไปก่อน ตัว EAS Build อ่าน app.json เพื่อรู้ bundle identifier, version, และค่า config อื่น ๆ ถึงจะ build ได้ ผมเคยเสียเวลาเป็นวันเพราะลืมขั้นนี้ใน monorepo ที่ migrate มาจาก React Native CLI (เปิด log แล้วงงอยู่นานว่าทำไม build ไม่อ่าน app.json เลย)

โครงสร้าง eas.json และ build profile

หัวใจของ EAS Build คือไฟล์ eas.json ที่นิยาม build profile หลายชุดในไฟล์เดียว สามชุดที่ผมใช้แทบทุกโปรเจกต์คือ development (dev client ภายใน), preview (TestFlight หรือ internal track), และ production (สโตร์จริง) แต่ละโปรไฟล์มี distribution, channel ของ EAS Update และ environment variable เป็นของตัวเอง

{
  "cli": {
    "version": ">= 14.0.0",
    "appVersionSource": "remote"
  },
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal",
      "channel": "development",
      "ios": { "resourceClass": "m-medium" },
      "android": { "buildType": "apk" },
      "env": {
        "EXPO_PUBLIC_API_URL": "https://api.dev.example.com"
      }
    },
    "preview": {
      "distribution": "internal",
      "channel": "preview",
      "ios": { "simulator": false },
      "android": { "buildType": "apk" },
      "env": {
        "EXPO_PUBLIC_API_URL": "https://api.staging.example.com"
      }
    },
    "production": {
      "autoIncrement": true,
      "channel": "production",
      "ios": { "resourceClass": "m-large" },
      "env": {
        "EXPO_PUBLIC_API_URL": "https://api.example.com"
      }
    }
  },
  "submit": {
    "production": {
      "ios": {
        "ascAppId": "1234567890",
        "appleTeamId": "ABCDE12345"
      },
      "android": {
        "serviceAccountKeyPath": "./secrets/play-service-account.json",
        "track": "production"
      }
    }
  }
}

ค่าที่ผมแนะนำให้ตั้งเป็นมาตรฐานคือ appVersionSource: "remote" เพื่อให้ EAS เก็บ version และ build number ไว้บนคลาวด์ แล้ว autoIncrement ใน production profile จะเพิ่ม build number ให้อัตโนมัติทุกครั้งที่ build ป้องกันเหตุการณ์ที่นักพัฒนาสองคน submit ทับด้วย build number เดียวกัน ซึ่งจะถูก App Store Connect ปฏิเสธทันที (เคยโดนมาเอง ตอน rebase แล้วลืม pull version ใหม่)

ผมแยก EXPO_PUBLIC_API_URL ตาม profile เสมอเพื่อให้ build ที่ทดสอบใน TestFlight ไม่ชี้ไป production API โดยบังเอิญ ค่า env ที่ขึ้นต้นด้วย EXPO_PUBLIC_ จะถูก inline ใน bundle ตอน build ดังนั้นห้ามใส่ secret หรือ API key ที่ละเอียดอ่อนในนี้ ให้ใช้ EAS Secrets ผ่าน eas secret:create สำหรับค่าที่ต้องป้องกันแทน

EAS Build ใช้ยังไง ตั้งแต่ build แรกถึง production

เมื่อ eas.json พร้อมแล้ว สั่ง build ด้วยคำสั่งเดียวต่อแพลตฟอร์ม ผมมักรัน parallel ทั้งสองเพื่อประหยัดเวลา queue โดยใช้ --non-interactive ใน CI หรือ local ก็ได้

npx eas build --profile production --platform ios
npx eas build --profile production --platform android
# หรือสั่งพร้อมกัน
npx eas build --profile production --platform all

ครั้งแรกที่รัน CLI จะถามเรื่อง credentials ผมแนะนำให้เลือก "Let Expo handle the process" เสมอสำหรับทีมขนาดเล็กถึงกลาง EAS จะสร้าง Apple distribution certificate, provisioning profile และ Android keystore เก็บไว้ใน secure vault ของ Expo เรียกใช้ได้จากทุกเครื่องโดยไม่ต้องส่งไฟล์ทาง Slack การเก็บ keystore เองยังทำได้ผ่าน eas credentials ถ้าทีมมีนโยบาย key custody ของตัวเอง

หลังสั่ง build CLI จะอัปโหลด source archive แล้วคืน URL ของ build job บน expo.dev ใช้เวลา iOS production ประมาณ 12-18 นาที, Android ราว 8-12 นาทีในเครื่อง m-large ผลลัพธ์เป็นไฟล์ .ipa สำหรับ iOS และ .aab สำหรับ Android พร้อม download URL ที่อยู่ได้ 30 วัน รายละเอียดล่าสุดดูได้ใน EAS Build introduction

การ build ผ่าน custom dev client

เมื่อโปรเจกต์มี native module นอกชุด Expo Go (เช่น react-native-mlkit, react-native-vision-camera ที่กำหนด config plugin) คุณต้อง build dev client ของตัวเอง สั่ง eas build --profile development --platform ios จะได้ .ipa ที่ติดตั้งบน iPhone จริงผ่าน TestFlight หรือ ad hoc แล้วเปิดด้วย Expo Go เวอร์ชัน custom ของคุณ ใช้คู่กับ npx expo start --dev-client เพื่อ reload bundle จาก Metro ปกติ ผมแนะนำให้ทุกโปรเจกต์ที่ตั้งใจไป production ใช้ custom dev client ตั้งแต่ต้น เพราะมันใกล้เคียงกับ binary ที่ลูกค้าใช้มากกว่า Expo Go มาตรฐาน

EAS Submit ขึ้น App Store และ Google Play อัตโนมัติ

EAS Submit รับ build จาก EAS Build แล้ว upload ขึ้น App Store Connect (iOS) หรือ Google Play Console (Android) โดยตรง ไม่ต้องเปิด Transporter หรือ Play Console UI สั่งงานด้วยคำสั่งเดียว

npx eas submit --profile production --platform ios --latest
npx eas submit --profile production --platform android --latest

ฟล็ก --latest หยิบ build ล่าสุดจาก EAS Build มาส่งโดยอัตโนมัติ ถ้าต้องการเลือก build เฉพาะให้ใช้ --id <build-id> ส่วน iOS ครั้งแรก CLI จะขอ App Store Connect API key (ไฟล์ .p8) ที่สร้างจาก App Store Connect → Users and Access → Keys หลังจากนั้น EAS จะเก็บ key ไว้ใช้ครั้งต่อ ๆ ไปโดยอัตโนมัติ

สำหรับ Android ต้องสร้าง service account ใน Google Cloud Console พร้อมสิทธิ์ "Release manager" บน Play Console แล้วชี้ path ของไฟล์ .json ใน eas.json ภายใต้ submit.production.android.serviceAccountKeyPath ผมแนะนำให้เก็บไฟล์นี้ไว้ในตำแหน่งที่ .gitignore exclude แล้วโหลดผ่าน secret store ของ CI แทน ห้าม commit เข้า repo เด็ดขาด เพราะมันเทียบเท่า master key ของ Google Play account ของคุณ

หลัง submit สำเร็จ build จะปรากฏใน TestFlight ภายใน 5-15 นาที (รอ Apple ทำ post-processing) ส่วน Google Play track ใช้เวลาน้อยกว่ามาก ราว 1-2 นาที สำหรับการตั้ง strategy การทดสอบก่อนกด release แนะนำให้ส่ง preview profile ขึ้น internal track ของ Google และ TestFlight internal group ก่อนเสมอ ทีมผมตั้งเงื่อนไขว่า build จะขึ้น production track ก็ต่อเมื่อผ่าน Maestro flow ครบทุก step บน TestFlight แล้วเท่านั้น

EAS Update และ runtime version สำหรับ OTA

EAS Update คือกลไกกระจาย JavaScript bundle และ asset (เช่น รูป, ฟอนต์) ใหม่โดยไม่ต้องส่ง binary ขึ้นสโตร์อีกครั้ง เหมาะกับการแก้บั๊กเล็ก, ปรับ copy, หรือเปลี่ยน UI ที่ไม่กระทบ native module หลักการคือแอปที่ติดตั้งบนเครื่องจะดาวน์โหลด update bundle ตอน start แล้วโหลด JS layer ใหม่บนพื้นฐาน native ของ build เดิม

กุญแจสำคัญคือ runtime version ค่านี้บอกว่า JS bundle ที่กระจายเข้ากันได้กับ native layer เวอร์ชันไหนบ้าง ตั้งใน app.json เช่นนี้

{
  "expo": {
    "runtimeVersion": {
      "policy": "fingerprint"
    },
    "updates": {
      "url": "https://u.expo.dev/your-project-id"
    }
  }
}

ตั้งแต่ Expo SDK 52 ขึ้นมาผมเปลี่ยนจาก policy sdkVersion เป็น fingerprint เพราะมัน hash native dependency ทั้งหมดและสร้าง runtime version อัตโนมัติ ทำให้ EAS รู้ทันทีว่า build ไหนเข้ากับ update ไหน ถ้าคุณเพิ่ม native module ใหม่หลัง release ฟีเจอร์ fingerprint จะเปลี่ยน runtime version แล้วกัน update จากการเผยแพร่ไป build เก่าโดยอัตโนมัติ (ผมเจอบั๊กนี้เองตอนยังใช้ sdkVersion แล้วผู้ใช้รุ่นเก่าได้ JS ใหม่ที่เรียก native API ที่ยังไม่มี ทำให้แอป crash ทันทีที่เปิด)

npx eas update --branch production --message "fix: typo in checkout button"

คำสั่งนี้ build JS bundle, อัปโหลดขึ้น Expo CDN และเชื่อมกับ channel ของ build profile ที่ตั้งไว้ใน eas.json เครื่องผู้ใช้จะดึง update ตอนเปิดแอปครั้งถัดไป (default behavior) หรือคุณจะเรียก Updates.checkForUpdateAsync() เพื่อบังคับเช็คทันทีก็ได้ ผมมักผูก rollout strategy โดยใช้ eas channel:edit ปรับ percentage rollout ทีละ 10-25% เพื่อรองรับการ rollback ถ้ามีปัญหา

EAS Workflows สำหรับ CI/CD ในไฟล์เดียว

EAS Workflows ที่ออก GA ต้นปี 2026 ทำหน้าที่ orchestrator ผูก build, submit, update และ test เข้าด้วยกันในไฟล์ YAML วางไว้ใน .eas/workflows/release.yml ตัวอย่างที่ผมใช้กับโปรเจกต์ production

name: Release production
on:
  push:
    branches: ["main"]
jobs:
  test:
    type: test
    steps:
      - run: npm ci
      - run: npm run typecheck
      - run: npm test -- --ci
  build_ios:
    needs: [test]
    type: build
    params:
      platform: ios
      profile: production
  build_android:
    needs: [test]
    type: build
    params:
      platform: android
      profile: production
  submit:
    needs: [build_ios, build_android]
    type: submit
    params:
      profile: production
  ota_hotfix:
    if: ${{ contains(github.event.head_commit.message, '[hotfix]') }}
    type: update
    params:
      branch: production
      message: ${{ github.event.head_commit.message }}

ผมเลือกใช้ EAS Workflows แทน GitHub Actions เพราะมัน run บน worker ของ Expo ที่มี Xcode และ Android SDK ติดตั้งไว้แล้ว ไม่ต้องเสียเวลา install toolchain ทุก run ค่าใช้จ่ายนับเป็น build minutes รวมกับ EAS Build โดยไม่บวกเพิ่ม สำหรับทีมที่ใช้ GitHub Actions อยู่แล้ว Expo มี action ชื่อ expo-github-action ที่เรียก eas-cli ได้เช่นกัน แต่จะเสีย runner minutes ของ GitHub แทน ทีมเล็ก ๆ ที่ release รายสัปดาห์ขึ้นไปคุ้มที่จะ migrate มา Workflows เพราะลด overhead ของการ maintenance pipeline ได้ชัด

EAS Build vs Expo Classic Build ต่างกันยังไง

Expo Classic Build (turtle CLI) ปิดบริการไปแล้วตั้งแต่มกราคม 2023 ทีมที่ยังใช้อยู่ในปี 2026 จำเป็นต้องย้ายมา EAS Build ความแตกต่างเชิงเทคนิคที่ผมเจอบ่อยตอนทำ migration คือ

มิติEAS BuildClassic Build (เลิกใช้)
โปรเจกต์ที่รองรับManaged และ bare workflowManaged เท่านั้น
การกำหนดค่าeas.json + build profileCLI flag อย่างเดียว
Custom native codeรองรับเต็มผ่าน config pluginไม่รองรับ
ระบบจัดการ credentialsSecure vault ของ Expo หรือ self-managedExpo เก็บให้แบบจำกัด
คิดค่าบริการBuild minutes + plan tierฟรีจำกัด queue
ความเร็วiOS ~15 นาที, Android ~10 นาทีหลายชั่วโมงเมื่อ queue ยาว
OTA UpdateEAS Update + runtime versionClassic Updates (ปิดบริการ)

ข้อแตกต่างที่สำคัญที่สุดสำหรับ production คือ EAS Build รองรับ bare workflow และ config plugin ทำให้คุณใช้ native module นอกชุด Expo SDK ได้โดยไม่ต้องเลิกใช้บริการคลาวด์ ทีมที่กำลังพิจารณา Expo Router และ React Navigation v7 ก็ได้ประโยชน์เต็มที่จาก EAS เพราะทั้งคู่ออกแบบมาทำงานคู่กัน config plugin ของ router จะถูก resolve ตอน prebuild บนคลาวด์อัตโนมัติ

ค่าบริการ EAS และโควตา build minutes

EAS ใช้โมเดล subscription รายเดือนผูกกับ organization ไม่ใช่ต่อแอป สำหรับนักพัฒนาเดี่ยวที่ปล่อย build ไม่บ่อย Free tier ให้ build queue priority ต่ำกว่าและจำกัด concurrency แต่พอใช้สำหรับ side project ขนาดเล็ก แผน Production ($99/เดือนในปี 2026) ปลดล็อก priority queue และ build minutes มากขึ้น เหมาะกับทีมที่ release รายสัปดาห์ ส่วนทีมเอเจนซีหรือ enterprise ใช้แผน On-demand ที่คิดตาม build minutes ล้วน ๆ

เคล็ดลับประหยัด build minutes ที่ผมใช้ คือเปลี่ยน resourceClass ตาม workload จริง m-medium พอสำหรับ Android และ dev client แต่ Hermes compilation ของ iOS production มักได้ผลลัพธ์ดีกว่าบน m-large เพราะ build เร็วกว่า 25-30% แม้ราคาต่อ minute สูงกว่า รวมเวลาแล้วประหยัดทั้งเงินและรอบทำงาน อ้างอิงรายละเอียดล่าสุดที่ Expo Build infrastructure docs

อีกตัวที่ลดต้นทุนได้คือ EAS Update ใช้ฟรี 1,000 monthly active users ในแผน Free และเพิ่มเป็น 50,000 MAU ในแผน Production ทำให้แอปขนาดเล็กถึงกลางใช้ OTA โดยไม่บวกค่าใช้จ่าย MAU นับเฉพาะเครื่องที่ดึง manifest จริง ไม่ใช่จำนวน install ทั้งหมด อ่านนิยามทางการได้ใน EAS Update introduction

คำถามที่พบบ่อย

EAS Build ฟรีไหม และมีข้อจำกัดอะไรบ้าง?

Free tier ของ EAS Build ใช้ได้ฟรีโดยไม่จำกัดจำนวน build แต่จะอยู่ใน queue ที่มี priority ต่ำกว่า paid tier อาจรอคิว 5-30 นาทีในช่วงเวลาใช้งานหนาแน่น และมี concurrency จำกัดที่ 1 build พร้อมกัน แผน Production ($99/เดือนในปี 2026) ให้ priority queue และ concurrency หลายอัน เหมาะกับทีมที่ release รายสัปดาห์ขึ้นไป

EAS Update ต่างจาก CodePush ยังไง?

Microsoft ประกาศปิดบริการ CodePush ในเดือนมีนาคม 2025 ทำให้ EAS Update เป็นทางเลือกหลักของชุมชน React Native ในปี 2026 EAS Update ใช้ runtime version policy แบบ fingerprint เพื่อป้องกัน update ไปทับ native build ที่ไม่เข้ากันโดยอัตโนมัติ ขณะที่ CodePush ใช้ deployment key คงที่ซึ่งจัดการ compatibility ด้วยตนเอง

ต้องมี Apple Developer account ของตัวเองก่อนใช้ EAS Submit ไหม?

จำเป็นสำหรับ iOS เสมอ คุณต้องมี Apple Developer Program ($99/ปี) เพื่อรับ App Store Connect API key ที่ EAS Submit ใช้สำหรับ upload build ส่วน Android ใช้ Google Play Developer account ($25 ครั้งเดียว) พร้อม service account JSON สำหรับสิทธิ์ Release Manager EAS เพียงจัดการ credential ให้ ไม่ได้แทน developer account

Build บน EAS ใช้เวลานานเท่าไหร่?

โดยเฉลี่ย iOS production build บน m-large ใช้ 12-18 นาที, Android production บน m-medium ใช้ 8-12 นาที ความเร็วขึ้นกับขนาดโปรเจกต์, จำนวน native dependency และ Hermes compilation ถ้า build เกิน 30 นาทีบ่อย ๆ ให้ตรวจ node_modules ที่บวมและ remove dev dependency ที่ไม่จำเป็น

EAS Build รองรับ monorepo (pnpm, Turborepo, Nx) ไหม?

รองรับเต็มที่ในปี 2026 EAS Build อ่าน package.json workspace อัตโนมัติเมื่อกำหนด cli.requireCommit: true และตั้ง root directory ของแอปใน eas.json ส่วน pnpm ต้องเพิ่ม .npmrc ที่มี node-linker=hoisted เพราะ build worker ของ Expo ใช้ npm หรือ yarn เป็นค่าเริ่มต้น อ่านรายละเอียดเพิ่มเติมที่ Expo monorepo guide

Jake Morrison
เกี่ยวกับผู้เขียน Jake Morrison

React Native lead engineer who's shipped six apps and learned six different lessons. Bullish on the New Architecture.