Deploying docker-hello-world
Table of Contents
- What this example is for
- Run it locally
- Build and push the image
- Deploy it as a stack
- Beginner Pitfalls
1. What this example is for
Run App has no API — there's nothing to wrap in code. docker-hello-world
exists to give the dashboard steps from
Part 1
something concrete to deploy and configure, not to demonstrate a Run App
SDK.
It's a small Express app: a static frontend, a JSON API, and a health endpoint.
const express = require("express");
const path = require("path");
const app = express();
const port = process.env.PORT || 3000;
const message = process.env.APP_MESSAGE || "Hello from Run App!";
const accentColor = process.env.APP_COLOR || "#2563eb";
let visitCount = 0; // in-memory only — resets on restart/redeploy
const startedAt = Date.now();
app.use(express.static(path.join(__dirname, "..", "public")));
app.use(express.json());
app.get("/api/config", (req, res) => {
res.json({ message, accentColor });
});
app.get("/api/counter", (req, res) => {
res.json({ count: visitCount });
});
app.post("/api/counter/increment", (req, res) => {
visitCount += 1;
res.json({ count: visitCount });
});
app.get("/health", (req, res) => {
res.json({
status: "ok",
uptimeSeconds: Math.floor((Date.now() - startedAt) / 1000),
});
});
app.listen(port, () => {
console.log(`Listening on port ${port}, APP_MESSAGE="${message}"`);
});
It reads PORT, APP_MESSAGE, and APP_COLOR — this mirrors Run App's
Shared Env / per-service Environment step from Part 1: you set these as
env vars when creating the stack and see them reflected once deployed. The
visit counter is a plain module-level variable, deliberately in-memory —
there's no database service in this series yet, so it exists to make
"state" visible and testable, and resets on every container restart or
redeploy.
2. Run it locally
nvm use && npm install
npm run dev
Visit http://localhost:3000, click "Visit +1" a few times, then restart
the server — the counter resets to 0. Try
APP_MESSAGE="hi" APP_COLOR="crimson" npm run dev and refresh to see both
change.
3. Build and push the image
The Dockerfile is the only thing Run App actually consumes — you build
and push this image to a registry, then reference it in the create-stack
wizard:
FROM node:20-alpine
WORKDIR /app
COPY package.json ./
RUN npm install --omit=dev
COPY src ./src
COPY public ./public
EXPOSE 3000
CMD ["node", "src/server.js"]
Run App currently only supports linux/amd64, so build explicitly for
that platform even on an Apple Silicon Mac:
docker login
docker buildx build --platform linux/amd64 \
-t <your-dockerhub-username>/run-app-hello:latest \
--push .
--push builds and pushes in one step. To inspect the image locally
first, load it into Docker instead and push manually:
docker buildx build --platform linux/amd64 \
-t <your-dockerhub-username>/run-app-hello:latest \
--load .
docker push <your-dockerhub-username>/run-app-hello:latest
4. Deploy it as a stack
Follow Part 1
to create a stack from <your-dockerhub-username>/run-app-hello:latest,
setting APP_MESSAGE and APP_COLOR as Shared Env keys so you can see
them reflected once deployed. Optionally continue with
Part 3
to attach a custom domain.
5. Beginner Pitfalls
- Building without
--platform linux/amd64on Apple Silicon. The resulting image isarm64and won't run on Run App's infrastructure — always build explicitly forlinux/amd64. - Expecting the visit counter to survive a redeploy. It's in-memory by design — a real persistent counter would need a database service, which isn't part of this series yet.
- Forgetting to set
APP_MESSAGE/APP_COLORas env vars on the stack. Without them, the app still runs — it just falls back to its defaults — so this is easy to miss rather than error out on.
Buy Me a Coffee
Found this tutorial helpful? Consider buying me a coffee to support more free content!