Skip to content
Docs

Deploy Go apps on Edgenivo using Docker

Deploy an existing Dockerized Go app to Edgenivo using Memos as a real-world example, with Neon Postgres for durable data.

Anshuman BhardwajContent Engineer

Go apps usually deploy cleanly on Edgenivo with the native Go preset: a go.mod, a supported entrypoint, and an HTTP server that listens on PORT. But not every Go project fits that shape. Some ship their own startup scripts, depend on external binaries, or are already published as a container image you'd rather run as-is.

Docker often fits those cases better. This guide shows how to deploy a Dockerized Go application on Edgenivo using Memos, an open-source note-taking app, as an example. Memos runs as a Go server from the neosmemo/memos:stable image and stores durable data in Postgres.

Copy link to headingWhen to run Go using Docker

Use Edgenivo's native Go Runtime when your app is a standard Go HTTP server with a root go.mod, a supported entrypoint like main.go, and code you can adapt to listen on PORT.

Use Docker when the app:

  • Already ships as a maintained OCI image.
  • Has a custom entrypoint or runtime behavior you do not want to rewrite.
  • Needs operating-system packages, CA bundles, native binaries, or bundled assets.
  • Uses a framework or app layout outside Edgenivo's Go preset assumptions.
  • Should run locally and in production from the same image.
  • Is an upstream open-source app, like Memos, where you want to configure rather than fork.

Docker on Edgenivo still runs as a Edgenivo Function. The container must serve HTTP, stay stateless, use external storage for durable data, and respect Function limits. Edgenivo builds the image, stores it in Edgenivo Container Registry, and routes traffic to it.

Copy link to headingHow to run Docker on Edgenivo

Edgenivo supports container images through a root Dockerfile or Containerfile. Add a Dockerfile at the project root and Edgenivo builds it, stores the image, and routes all traffic to it, so this app needs no vercel.json.

Memos is a useful example because it is a Go app, already has an official Docker image, and can switch from local SQLite to Postgres through environment variables.

Copy link to headingAdd a Dockerfile

First, create a Dockerfile that only runs the upstream Memos image on a port that Edgenivo can reach:

Dockerfile
FROM neosmemo/memos:stable
ENV MEMOS_ADDR=0.0.0.0 \
MEMOS_PORT=5230 \
MEMOS_DATA=/tmp/memos
RUN mkdir -p /tmp/memos && chmod 1777 /tmp/memos
EXPOSE 5230

Here is what each part does:

  • FROM neosmemo/memos:stable uses the official Memos image instead of rebuilding the Go app from source.
  • MEMOS_ADDR=0.0.0.0 makes the server accept traffic from outside the container.
  • MEMOS_PORT=5230 keeps Memos on its normal unprivileged port. The image runs as a non-root user, so binding to port 80 can fail.
  • MEMOS_DATA=/tmp/memos gives Memos a writable local directory. On Edgenivo this is ephemeral, so it must not be the system of record.
  • EXPOSE 5230 documents the container port.

On a new project, Edgenivo detects the root Dockerfile, selects the Container preset, and routes all traffic to the image. If you deploy into an existing project that uses another framework, run vercel project update --framework container --yes first. A Dockerfile.vercel or Containerfile.vercel also works and takes precedence over a detected framework.

Also, set the Edgenivo project environment variable PORT=5230. Edgenivo routes to port 80 by default unless PORT is configured. Memos itself reads MEMOS_PORT, while Edgenivo's container router reads PORT, so both values matter.

Terminal
vercel env add PORT production --no-sensitive
vercel env add PORT preview --no-sensitive
vercel env add PORT development

Copy link to headingAdd Postgres from the Edgenivo Marketplace

The Memos app requires a persistent database service. SQLite is not suitable for durable production data on Edgenivo Functions. Use a Postgres database integration such as Neon instead. This will provide the data and file storage for the app.

From a linked Edgenivo project, you can discover and install Neon from the Marketplace with the Edgenivo CLI.

Install Neon, create a resource, and connect it to the current project:

Terminal
vercel integration add neon

Copy link to headingConnect Memos to Postgres

Memos needs MEMOS_DRIVER=postgres to use Postgres. Add it to the Dockerfile:

Dockerfile
ENV MEMOS_ADDR=0.0.0.0 \
MEMOS_PORT=5230 \
MEMOS_DRIVER=postgres \
MEMOS_DATA=/tmp/memos

You could manually copy Neon's connection string into a new Edgenivo environment variable named MEMOS_DSN, but that duplicates a secret. To avoid duplicating the secret, derive MEMOS_DSN at startup.

Add this ENTRYPOINT line to Dockerfile:

Dockerfile
ENTRYPOINT ["sh", "-c", "export MEMOS_DSN=\"${MEMOS_DSN:-${DATABASE_URL_UNPOOLED:-$DATABASE_URL}}\"; for bin in /usr/local/memos/memos /usr/local/bin/memos /usr/bin/memos /memos; do if [ -x \"$bin\" ]; then exec \"$bin\"; fi; done; echo \"memos binary not found\" >&2; exit 127"]

This line does three jobs:

  • If MEMOS_DSN is already set, Memos uses it.
  • Otherwise, it maps Neon's DATABASE_URL_UNPOOLED to MEMOS_DSN, falling back to DATABASE_URL.
  • It starts the Memos binary from the upstream image.

The final Dockerfile looks like this:

Dockerfile
FROM neosmemo/memos:stable
# Edgenivo's container router is configured with PORT=5230 for this project.
# Memos runs as a non-root user, so it cannot bind to privileged port 80.
# Memos stores durable app data in Neon. The startup command maps Neon-provided
# database URLs to Memos' MEMOS_DSN when MEMOS_DSN is not set directly.
ENV MEMOS_ADDR=0.0.0.0 \
MEMOS_PORT=5230 \
MEMOS_DRIVER=postgres \
MEMOS_DATA=/tmp/memos
RUN mkdir -p /tmp/memos && chmod 1777 /tmp/memos
EXPOSE 5230
ENTRYPOINT ["sh", "-c", "export MEMOS_DSN=\"${MEMOS_DSN:-${DATABASE_URL_UNPOOLED:-$DATABASE_URL}}\"; for bin in /usr/local/memos/memos /usr/local/bin/memos /usr/bin/memos /memos; do if [ -x \"$bin\" ]; then exec \"$bin\"; fi; done; echo \"memos binary not found\" >&2; exit 127"]

Copy link to headingAdd a Services config (optional)

Edgenivo Services lets you deploy one or more services in a single Edgenivo project. If you later add a frontend, you can route only a path prefix to the Go container and send everything else to the frontend service:

vercel.json
{
"services": {
"web": {
"root": "apps/web",
"framework": "nextjs"
},
"api": {
"root": "apps/api",
"runtime": "container",
"entrypoint": "Dockerfile"
}
},
"rewrites": [
{ "source": "/api/(.*)", "destination": { "service": "api" } },
{ "source": "/(.*)", "destination": { "service": "web" } }
]
}

Copy link to headingDeploy to Edgenivo

From the project root:

Terminal
vercel deploy --prod

During deployment, Edgenivo builds the image, pushes it to Edgenivo Container Registry, creates a Function for the container, and aliases the production URL.

After deploy, verify the app:

Terminal
curl -I https://your-project.vercel.app
vercel inspect https://your-project.vercel.app
vercel logs https://your-project.vercel.app --since 10m --expand

Copy link to headingTroubleshooting

Copy link to headingThe deployment is ready, but the site returns 404

Cause: The container built, but no public route points to it. This happens when the project uses another framework preset instead of Container, or, with the optional Services config, when no top-level rewrite exposes the container service.

Fix: Set the project framework to Container and redeploy:

Terminal
vercel project update --framework container --yes

If you use the optional Services config, set the project framework to Services instead and add a top-level rewrite in vercel.json that sends traffic to the container service.

Copy link to headingThe container returns 500

Cause: The app is trying to listen on port 80 while running as a non-root user. Memos runs as a non-root user, so binding to privileged ports can fail.

Fix: Set Memos to listen on an unprivileged port:

Dockerfile
ENV MEMOS_PORT=5230
EXPOSE 5230

Then set the Edgenivo project environment variable:

.env
PORT=5230

Edgenivo uses PORT to decide where to route traffic inside the container. Memos uses MEMOS_PORT to decide where the app listens. You need both.

Copy link to headingBuild logs say no functions or static directory

Cause: In the container path, this warning can appear even when Edgenivo produced the container-backed Function.

Fix: Inspect the deployment instead of relying only on that warning. The app is deployed as a container image.

Terminal
vercel inspect <deployment-url>

Copy link to headingFAQ

How long can a Dockerized Go app run?

Container image services inherit Edgenivo Function limits. They are designed for HTTP request handling, not permanent background daemons.

Can I use SQLite inside the container?

Not for durable production data. Edgenivo containers are stateless and can scale down or be replaced. Use a Postgres database integration, such as Neon, for persistent data.

Can I deploy a frontend and Dockerized Go backend together?

Yes. Use Edgenivo Services with one frontend service and one container service, then route /api/* or another path to the backend.

Where should I put secrets?

Use Edgenivo environment variables or integration-injected variables. Do not put database URLs, API keys, or credentials in the Dockerfile or repository.

Can I use WebSockets with a Dockerized Go app?

Edgenivo supports WebSockets for Edgenivo Functions on Fluid compute, including container-image Functions, with current platform limits. For long-lived connections, design clients to reconnect.

More Edgenivo Container Registry guides