How to Deploy Fishjam to Production
How-to Guide - Deploy your Fishjam backend safely to production
This guide covers the essential steps to move from development (using Room Manager) to a production-ready Fishjam backend deployment.
Prerequisites
- Working Fishjam backend (see Backend Quick Start)
- Production Fishjam environment (not Sandbox)
- Domain and SSL certificates for your backend
- Production database and infrastructure
Step 1: Set up production Fishjam environment
Get production credentials
- Log in to Fishjam Dashboard
- Create or select your Production environment (not Sandbox)
- Copy your production Fishjam URL and Management Token
- Note: These are different from your sandbox credentials
Environment variables setup
Create production environment variables:
# Production Fishjam credentials FISHJAM_URL="your-production-fishjam-url" FISHJAM_MANAGEMENT_TOKEN="your-production-management-token" # Your application settings NODE_ENV="production" PORT="3000" DATABASE_URL="your-production-database-url" # Security settings JWT_SECRET="your-secure-jwt-secret" CORS_ORIGIN="https://yourdomain.com"
Step 2: Implement proper authentication
Replace Room Manager calls
Remove any Room Manager dependencies from your client code:
// ❌ Remove: Room Manager calls const
response = awaitfetch ( `https://fishjam.io/api/v1/connect/${ROOM_MANAGER_ID }/room-manager?...`, ); // ✅ Replace with: Your authenticated API constresponse = awaitfetch ("/api/join-room", {method : "POST",headers : { "Content-Type": "application/json",Authorization : `Bearer ${userToken }`, },body :JSON .stringify ({roomName }), });
Implement user authentication
const
fishjamClient = newFishjamClient ({fishjamUrl :process .env .FISHJAM_URL !,managementToken :process .env .FISHJAM_MANAGEMENT_TOKEN !, }); // Authentication middleware constauthenticateUser = async (req :express .Request ,res :express .Response ,next :express .NextFunction , ) => { try { consttoken =req .headers .authorization ?.replace ("Bearer ", ""); if (!token ) { returnres .status (401).json ({error : "Authentication required" }); } constdecoded =jwt .verify (token ,process .env .JWT_SECRET );req .user = awaitgetUserById (decoded .userId );next (); } catch (error ) {res .status (401).json ({error : "Invalid token" }); } }; // Protected endpoint for joining roomsapp .post ("/api/join-room",authenticateUser , async (req ,res ) => { try { const {roomName } =req .body ; constuser =req .user ; // Create or get room constroom = awaitgetOrCreateRoom (roomName ); // Verify user has permission to join this room if (!canUserJoinRoom (user ,room )) { returnres .status (403).json ({error : "Access denied" }); } // Create peer with user metadata const {peer ,peerToken } = awaitfishjamClient .createPeer (room .id , {metadata : {userId :user .id ,name :user .name ,avatar :user .avatar ,role :user .role , }, });res .json ({peerToken ,fishjamUrl :process .env .FISHJAM_URL , }); } catch (error ) {console .error ("Join room error:",error );res .status (500).json ({error : "Failed to join room" }); } });
(Optional) Step 3: Handle webhooks and events
You may wish to receive events from Fishjam regarding created rooms and peers. All you need for this is a single api endpoint:
Webhook endpoint
// Webhook signature verification const
verifyWebhookSignature = (req :express .Request ,res :express .Response ,next :express .NextFunction , ) => { constsignature =req .headers ["x-fishjam-signature"]; constpayload =JSON .stringify (req .body ); constexpectedSignature =crypto .createHmac ("sha256",process .env .WEBHOOK_SECRET ) .update (payload , "utf8") .digest ("hex"); if (signature !==expectedSignature ) { returnres .status (401).json ({error : "Invalid signature" }); }next (); }; // Webhook handlerapp .post ( "/api/webhooks/fishjam",express .raw ({type : "application/json" }),verifyWebhookSignature , (req :express .Request ,res :express .Response ) => { constevent =req .body ; switch (event .type ) { case "peer_connected":handlePeerConnected (event .data ); break; case "peer_disconnected":handlePeerDisconnected (event .data ); break; case "room_empty":handleRoomEmpty (event .data ); break; default:console .log ("Unhandled event type:",event .type ); }res .status (200).json ({received : true }); }, );
Enabling webhooks
Now, with your endpoint setup, all you need to do is supply your webhook endpoint to Fishjam when creating a room:
const
createRoomWithWebhooks = async (roomName : string,roomType = "conference", ) => { constroom = awaitfishjamClient .createRoom ({roomType ,webhookUrl : `${process .env .BASE_URL }/api/webhooks/fishjam`, }); returnroom ; };
Common production issues
Issue: Token expiration handling
Peer tokens expire 24h after creation. We encourage keeping room and peer lifetimes as short as possible (typically a single room corresponds to a single video call or stream). However, if you wish to reuse a single peer over multiple days, you can make use of token refreshing:
- Node.js / TypeScript
- Python
const
newToken =fishjamClient .refreshPeerToken (roomId ,peerId );
new_token = fishjam_client.refresh_peer_token(room_id, peer_id)
See also
For scaling considerations:
For specific backend frameworks: