The Deployment Handover
From Built Image to Running App
Section titled “From Built Image to Running App”A completed Docker build is important.
But it is still only a build.
At the end of the previous step, Render had successfully packaged the application image.
Now the platform has to do something different:
- create a container from that image
- inject the environment variables
- run the application command
- wait to see whether the app becomes a healthy web service
That is the deployment handover.
This is the moment where the packaged image stops being a build artifact and starts trying to become a live application.
What Changes in the Logs
Section titled “What Changes in the Logs”During the build phase, the log output is mostly Docker talking:
COPYRUN npm installRUN npm run buildCOPY --from=builder
Once the build completes, the log stream should change.
Now the app itself starts talking.
That means the logs should begin telling you about things like:
- application startup
- MongoDB connection
- server binding
- runtime failures, if they exist
Build logs tell you whether Render could assemble the image. Runtime logs tell you whether the application can actually wake up and behave like a hosted web service.
What Render Is Doing Now
Section titled “What Render Is Doing Now”Conceptually, the runtime sequence looks like this:
- the image build finishes
- Render starts a container from that image
- Render injects the configured environment variables
- the container runs the image command
- the Node process starts
- Express and Mongoose begin startup
- Render waits to see whether the service becomes healthy
That is the runtime checkpoint.
This is where the app proves whether it can actually live on the platform, not just compile nicely for it.
The Runtime Proofs of Life
Section titled “The Runtime Proofs of Life”For Voyager’s Log to become a healthy live service, a few things need to happen in the right order.
1) The App Starts
Section titled “1) The App Starts”The container must successfully run the command from the Dockerfile:
CMD ["node", "server/index.js"]If the process crashes immediately, the service will never become healthy.
2) MongoDB Connection Succeeds
Section titled “2) MongoDB Connection Succeeds”The app must be able to use MONGO_URI to connect to Atlas.
If that fails, the runtime logs will usually show connection errors, auth failures, or retry noise instead of a clean startup.
3) The Server Binds to the Provided Port
Section titled “3) The Server Binds to the Provided Port”The Express server must successfully listen using the port Render provides through the environment.
In code, that means something in this shape:
const PORT = process.env.PORT || 3000;app.listen(PORT, '0.0.0.0', () => { console.log(`Voyager's Log API listening on port ${PORT}`);});4) Render Recognizes the Service as Healthy
Section titled “4) Render Recognizes the Service as Healthy”Once the process is still alive and listening properly, Render can treat the service as healthy and begin routing traffic to it.
That is the real runtime milestone.
What Good Runtime Logs Tend to Look Like
Section titled “What Good Runtime Logs Tend to Look Like”The exact output depends on your app logging, but healthy runtime logs often include signs like:
- successful MongoDB connection
- a message showing the server is listening on a port
- no immediate crash loop or restart loop
Typical healthy output might resemble:
Connected to MongoDBVoyager's Log API listening on port 10000The exact port value may not be 3000, and that is normal. Render provides the runtime port dynamically, and the application is expected to accept it.
In hosted runtime, the important question is not whether the port looks familiar. The real question is whether the app successfully bound to the port Render gave it.
Why the Port May Look Strange
Section titled “Why the Port May Look Strange”Students often see a runtime log showing a port like 10000 and immediately assume something is broken.
Usually, it is not.
The public URL does not require your Node process to bind directly to 80 or 443. Render sits in front of the app and routes public HTTPS traffic to the internal port your container is listening on.
So the real test is not:
Is this the port I expected locally?
It is:
Did the process successfully bind to the port Render provided?
That is the question that matters.
If Runtime Fails Here
Section titled “If Runtime Fails Here”If the build succeeded but the service still fails during startup, the likely causes have shifted.
At this point, the common suspects are things like:
- incorrect
MONGO_URI - incorrect
SESSION_SECRET - missing
NODE_ENV - startup errors in application code
- inability to connect to Atlas
- failure to bind correctly to the provided port
That is why this page exists separately from the build page.
The build phase and runtime phase fail for different reasons. We need to keep those layers separate when debugging.
Build Phase
Section titled “Build Phase”Can the platform package the app?
Runtime Phase
Section titled “Runtime Phase”Can the platform actually run the app successfully?
These are not the same thing.
An image can build perfectly and still fail at runtime. A container can start and still never become healthy. A process can run and still not serve the app properly.
That distinction makes debugging much less chaotic.
Extra Bits & Bytes
Section titled “Extra Bits & Bytes”Render Docs: Health Checks
⏭ Verifying External Routing
A healthy runtime state is a strong signal, but it is not the final proof. Next, we verify the public URL directly and confirm that the hosted app is actually serving the compiled frontend correctly.