Deployment Guide
Complete guide to deploying Gridova backend, mobile app, and ESP32 firmware to production
Backend Deployment
Deploy to Vercel
Vercel is perfect for Next.js applications with serverless API routes.
1. Install Vercel CLI
terminalbash
npm install -g vercel2. Login to Vercel
terminalbash
vercel login3. Deploy
terminalbash
# Deploy to preview
vercel
# Deploy to production
vercel --prod4. Configure Environment Variables
terminalbash
# Via CLI
vercel env add DATABASE_URL
vercel env add JWT_SECRET
# Or via Vercel Dashboard:
# Project Settings → Environment Variables5. vercel.json Configuration
vercel.jsonjson
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/next"
}
],
"regions": ["sin1"],
"env": {
"NODE_ENV": "production"
},
"functions": {
"api/**/*.js": {
"memory": 1024,
"maxDuration": 10
}
}
}Vercel Limitations
Serverless functions have 10s timeout on Hobby plan, 60s on Pro. For long-running tasks (like WebSocket), consider using Vercel Edge Functions or external service.
Mobile App Deployment
Build and Publish to Google Play
1. Generate Keystore
terminalbash
keytool -genkey -v -keystore gridova-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias gridova
# Save the keystore file securely!
# Note the keystore password and key password2. Configure android/key.properties
android/key.propertiesproperties
storePassword=your_keystore_password
keyPassword=your_key_password
keyAlias=gridova
storeFile=../gridova-key.jks3. Update android/app/build.gradle
android/app/build.gradlegradle
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
compileSdkVersion 34
defaultConfig {
applicationId "com.gridova.app"
minSdkVersion 21
targetSdkVersion 34
versionCode 1
versionName "1.0.0"
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}4. Build Release APK/AAB
terminalbash
# Build APK
flutter build apk --release
# Or build App Bundle (recommended for Play Store)
flutter build appbundle --release
# Output files:
# APK: build/app/outputs/flutter-apk/app-release.apk
# AAB: build/app/outputs/bundle/release/app-release.aab5. Upload to Google Play Console
- Go to Google Play Console
- Create new app → Enter app details
- Production → Create new release
- Upload app-release.aab
- Fill in release notes and publish
Testing Before Release
Use Internal Testing or Closed Testing tracks to test with limited users before publishing to production.
ESP32 OTA Updates
Over-The-Air (OTA) updates allow you to update ESP32 firmware remotely without physical access.
ESP32 OTA Implementation
1. Enable OTA in Code
esp32_ota.inocpp
#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
void setup() {
Serial.begin(115200);
// Connect to WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
// Setup OTA
ArduinoOTA.setHostname("Gridova-ESP32-001");
ArduinoOTA.setPassword("admin"); // Change this!
ArduinoOTA.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else {
type = "filesystem";
}
Serial.println("Start updating " + type);
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
Serial.println("Auth Failed");
} else if (error == OTA_BEGIN_ERROR) {
Serial.println("Begin Failed");
} else if (error == OTA_CONNECT_ERROR) {
Serial.println("Connect Failed");
} else if (error == OTA_RECEIVE_ERROR) {
Serial.println("Receive Failed");
} else if (error == OTA_END_ERROR) {
Serial.println("End Failed");
}
});
ArduinoOTA.begin();
Serial.println("OTA Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void loop() {
ArduinoOTA.handle(); // Handle OTA updates
// Your code here
}2. Upload Firmware via OTA
terminalbash
# Find ESP32 devices
# Arduino IDE → Tools → Port → Network ports
# Or use espota.py
python espota.py -i 192.168.1.100 -p 3232 -f firmware.bin
# Or upload via Arduino IDE
# Select network port and click Upload3. HTTP OTA Update
http_ota.inocpp
#include <HTTPUpdate.h>
const char* firmwareUrl = "https://api.gridova.com/firmware/latest.bin";
void checkForUpdate() {
WiFiClientSecure client;
client.setInsecure(); // Skip certificate validation
httpUpdate.setLedPin(LED_BUILTIN, LOW);
t_httpUpdate_return ret = httpUpdate.update(client, firmwareUrl);
switch (ret) {
case HTTP_UPDATE_FAILED:
Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n",
httpUpdate.getLastError(),
httpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
Serial.println("HTTP_UPDATE_NO_UPDATES");
break;
case HTTP_UPDATE_OK:
Serial.println("HTTP_UPDATE_OK");
break;
}
}
void loop() {
// Check for updates daily
static unsigned long lastCheck = 0;
if (millis() - lastCheck > 86400000) { // 24 hours
checkForUpdate();
lastCheck = millis();
}
}OTA Safety Tips
- Always test firmware locally before OTA deployment
- Implement rollback mechanism in case of failed update
- Use strong password for OTA
- Verify firmware integrity (checksum/signature)
- Never interrupt power during OTA update
CI/CD Pipeline
GitHub Actions Workflow
.github/workflows/deploy.ymlyaml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy-backend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.ORG_ID }}
vercel-project-id: ${{ secrets.PROJECT_ID }}
vercel-args: '--prod'
deploy-mobile:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.22.0'
- name: Install dependencies
run: flutter pub get
- name: Run tests
run: flutter test
- name: Build APK
run: flutter build apk --release
- name: Upload to Play Store
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
packageName: com.gridova.app
releaseFiles: build/app/outputs/bundle/release/app-release.aab
track: productionProduction Checklist
Monitoring & Maintenance
Application Monitoring
- Sentry - Error tracking and performance monitoring
- New Relic - APM and infrastructure monitoring
- DataDog - Full-stack observability platform
- Prometheus + Grafana - Metrics and visualization
Uptime Monitoring
- UptimeRobot - Free uptime monitoring (5-minute checks)
- Pingdom - Website performance and uptime monitoring
- StatusPage - Public status page for users
Regular Maintenance Tasks
- Update dependencies weekly (security patches)
- Review and rotate API keys monthly
- Check database size and optimize queries
- Review error logs and fix recurring issues
- Test backup restoration quarterly
- Update SSL certificates before expiry
Support & Resources
Need Help?
Contact our support team:
- Email: support@gridova.com
- Documentation: docs.gridova.com
- Community Forum: community.gridova.com
