Deploy the Developer Portal
The API7 Developer Portal is made up of two official container images that work together:
| Image | Role | Port | Source repository |
|---|---|---|---|
api7/api7-ee-developer-portal | Portal API (backend): the Go service that stores portal definitions, API products, developers, applications, subscriptions, and credentials. It shares the main api7ee PostgreSQL database with the rest of the control plane. | 4321 (HTTPS) | api7ee-3-control-plane |
api7/api7-ee-developer-portal-fe | Developer Portal frontend: the customer-facing Next.js web app that developers sign in to. It uses its own PostgreSQL database for user sessions and developer accounts (via Better Auth and Drizzle), and communicates with the Portal API using a per-portal token. | 3001 | api7ee-developer-portal |
One backend serves your entire organization. You can deploy multiple frontend instances, one per portal, for separate branding or audiences.
The Portal API is part of the control plane and uses the same database as the Dashboard. This guide deploys the frontend and its separate session database, then connects the frontend to the existing Portal API. It also explains how to run another Portal API container when the control plane deployment does not already include one.
Prerequisites
- API7 Enterprise installed and activated with a portal-enabled license (trial licenses include portal support by default).
- Network connectivity from the frontend host to the Portal API endpoint (
4321/tcp). - Docker and Docker Compose, or a Kubernetes cluster with Helm, depending on which deployment method you choose.
Create a Portal and Token
Before deploying the Developer Portal frontend, create a portal instance and generate a connection token from the API7 Dashboard.
- Log in to the API7 Dashboard.
- Navigate to Provider Portal > Add Portal.
- Provide a Name and the Public URL where the Developer Portal will be accessible (for example,
https://portal.example.com). - After creating the portal, open its Settings > Tokens tab.
- Click Generate New Token and save the token (format:
a7prt-xxxxxxxxxxxx). You will add this token to the frontend configuration in the next section.
Each portal has a unique public URL. A single portal can back one frontend instance or multiple frontend replicas that share the same token and user-session database.
Configure the Frontend
Create a working directory for the frontend configuration:
mkdir api7-developer-portal && cd api7-developer-portal
mkdir -p frontend_conf
Frontend Configuration
The frontend connects to the Portal API using the token you generated above. It stores authentication and session data in a separate PostgreSQL database.
portal:
url: https://YOUR_PORTAL_API_HOST:4321
token: a7prt-xxxxxxxxxxxx
db:
url: "postgres://portal:YOUR_PORTAL_DB_PASSWORD@portal-postgres:5432/portal"
auth:
secret: "REPLACE_WITH_SECURE_RANDOM_STRING"
app:
baseURL: "https://portal.example.com"
trustedOrigins:
- "https://portal.example.com"
❶ Set url to the existing Portal API endpoint. Its certificate must cover this host name. Use the token generated in the Provider Portal.
❷ Set the PostgreSQL connection string for the authentication and session database used by the frontend.
❸ Generate a secure authentication secret with openssl rand -base64 32.
❹ Set the public URL where developers access the portal. Add the same origin to trustedOrigins.
Replace auth.secret with a securely generated random value before running in any shared environment. Do not use the example value in production.
This file contains the Portal API token, session-database credentials, and authentication secret. Keep it out of version control.
The frontend verifies the Portal API certificate. Use a certificate issued by a CA that the frontend image trusts. For a private CA, mount its CA certificate into the frontend container and set NODE_EXTRA_CA_CERTS to the mounted file. Do not disable TLS verification for a production deployment.
For Docker Compose with a private CA, add the CA to the frontend service:
# The frontend runs as user ID 1001 and must be able to read this file.
chmod 644 certs/portal-api-ca.crt
services:
api7-developer-portal-frontend:
environment:
NODE_EXTRA_CA_CERTS: /app/certs/portal-api-ca.crt
volumes:
- ./frontend_conf/config.yaml:/app/apps/site/config.yaml:ro
- ./certs/portal-api-ca.crt:/app/certs/portal-api-ca.crt:ro
Deploy
Choose the deployment method that matches your environment.
- Docker Compose
- Kubernetes
Docker Compose is the quickest way to run the frontend and its session database for development or testing. The existing Portal API remains part of the control plane.
Create docker-compose.yaml in the same directory:
services:
portal-postgres:
image: postgres:16
hostname: portal-postgres
environment:
POSTGRES_USER: portal
POSTGRES_PASSWORD: YOUR_PORTAL_DB_PASSWORD
POSTGRES_DB: portal
volumes:
- portal_postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "portal", "-d", "portal"]
interval: 5s
timeout: 5s
retries: 5
networks:
- portal
api7-developer-portal-frontend:
image: api7/api7-ee-developer-portal-fe:${PORTAL_FE_VERSION}
hostname: api7-developer-portal-frontend
restart: always
volumes:
- ./frontend_conf/config.yaml:/app/apps/site/config.yaml:ro
ports:
- "3001:3001"
depends_on:
portal-postgres:
condition: service_healthy
networks:
- portal
networks:
portal:
driver: bridge
volumes:
portal_postgres_data:
After you finish editing the file, give the frontend user ownership. Compose preserves the host owner and mode:
# The frontend runs as user ID 1001.
sudo chown 1001 frontend_conf/config.yaml
# Mode 600 keeps the token and database password private.
sudo chmod 600 frontend_conf/config.yaml
Set the frontend image tag, then start the stack:
export PORTAL_FE_VERSION=v0.10.0
docker compose up -d
Frontend v0.10.0 is the version released with API7 Gateway 3.9.22. The Portal API follows the control-plane release version and is already running in the existing control plane.
Check that PostgreSQL reports healthy and the frontend reports Up:
docker compose ps
Run the Portal API separately
If the control plane deployment does not run the Portal API, you can run the matching api7/api7-ee-developer-portal:v3.9.22 image separately. Point its database.dsn at the existing control-plane database that contains the portal and token you created earlier. Do not create a new empty api7ee database for this service: the token is stored in the control-plane database and authentication fails when the Portal API cannot read it.
Create the directories for the backend configuration and TLS files:
mkdir -p backend_conf certs
Create backend_conf/conf.yaml with the existing database DSN and your TLS files:
server:
listen:
host: "0.0.0.0"
port: 4321
tls:
enabled: true
key_file: /app/certs/tls.key
cert_file: /app/certs/tls.crt
status:
disable: false
host: "127.0.0.1"
port: 4322
pprof:
enable: false
host: "127.0.0.1"
port: 6060
database:
dsn: "postgres://api7ee:YOUR_DB_PASSWORD@YOUR_CONTROL_PLANE_DB:5432/api7ee"
security:
ssrf_protection:
enable: false
allow_list: []
deny_list: []
❶ The status listener remains available on loopback for local health checks.
❷ The pprof listener is enabled on loopback by default; this example disables it. Enable it only for diagnostics and keep it private.
❸ Point the Portal API at the existing control-plane database that contains the portal and token.
❹ SSRF protection is disabled by default. Enable it to restrict outbound DCR and approval-webhook requests. Its allow and deny entries are CIDRs, and invalid CIDRs prevent startup.
Add the service to docker-compose.yaml:
services:
api7-developer-portal-backend:
image: api7/api7-ee-developer-portal:v3.9.22
volumes:
- ./backend_conf/conf.yaml:/usr/local/api7/conf/conf.yaml:ro
- ./certs:/app/certs:ro
command:
- -c
- /usr/local/api7/conf/conf.yaml
ports:
- "4321:4321"
networks:
- portal
Place the key and certificates in the certs directory before starting the service:
# The Portal API runs as user ID 65532.
sudo chown 65532 certs/tls.key
# Mode 600 is readable only by that owner.
sudo chmod 600 certs/tls.key
# Leave the certificate and CA readable by the Portal API and the frontend.
chmod 644 certs/tls.crt certs/portal-api-ca.crt
Make the issuing CA available to the frontend through NODE_EXTRA_CA_CERTS. If both containers use the portal network, set portal.url to the backend service name covered by that certificate, such as https://api7-developer-portal-backend:4321.
On Kubernetes, the main api7/api7ee3 control plane chart deploys the Portal API backend when developer_portal_configuration.enable: true (the default). You normally only need to deploy the frontend separately. This tab shows how to enable the backend in the control plane chart, then deploy the frontend as its own workload.
Enable the Backend in the Control Plane Chart
In your control plane values file (for example, cp-values.yaml), ensure the Developer Portal backend is enabled and configured:
developer_portal:
replicaCount: 1
keyCertSecret: api7-developer-portal-tls
image:
repository: api7/api7-ee-developer-portal
tag: "v3.9.22"
developer_portal_service:
type: ClusterIP
port: 4321
developer_portal_configuration:
enable: true
server:
listen:
host: "0.0.0.0"
port: 4321
tls:
enabled: true
key_file: /app/certs/tls.key
cert_file: /app/certs/tls.crt
status:
host: "127.0.0.1"
port: 4322
log:
level: warn
This excerpt intentionally does not replace developer_portal_configuration.database.dsn. Merge it into the existing control-plane values. The Portal API must keep the same chart-local or external database used by the Dashboard and DP Manager.
Create the TLS Secret before applying the chart values. The certificate must cover the Portal API host name used by the frontend. Include its issuing CA so the frontend can trust it:
kubectl create secret generic api7-developer-portal-tls \
--from-file=tls.key=portal-api-tls.key \
--from-file=tls.crt=portal-api-tls.crt \
--from-file=ca.crt=portal-api-ca.crt \
-n api7
Install or upgrade the control plane chart with these values:
helm upgrade --install api7ee3 api7/api7ee3 \
--version "3.9.11" \
-f cp-values.yaml \
-n api7
The backend is now reachable through its Service. Use a DNS name covered by its certificate in the frontend portal.url.
Deploy the Frontend
The published developer-portal-fe chart is version 0.1.0 and pins frontend v0.5.7, so it does not match the released v0.10.0 frontend used here. Deploy v0.10.0 as a standard Kubernetes workload instead. The manifest below uses a Secret to mount the config.yaml you created earlier.
Before applying the manifest, provision the frontend PostgreSQL database and a host or Kubernetes Service reachable as YOUR_PORTAL_DB_HOST. Create the portal database and user represented by the DSN. Replace the Portal API host and token, database host and password, authentication secret, public URL, and trusted origin in the Secret through your approved secret-management workflow.
apiVersion: v1
kind: Secret
metadata:
name: developer-portal-frontend-config
namespace: api7
type: Opaque
stringData:
config.yaml: |
portal:
url: https://YOUR_PORTAL_API_HOST:4321
token: a7prt-xxxxxxxxxxxx
db:
url: "postgres://portal:YOUR_PORTAL_DB_PASSWORD@YOUR_PORTAL_DB_HOST:5432/portal"
auth:
secret: "REPLACE_WITH_SECURE_RANDOM_STRING"
app:
baseURL: "https://portal.example.com"
trustedOrigins:
- "https://portal.example.com"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: developer-portal-frontend
namespace: api7
spec:
replicas: 2
selector:
matchLabels:
app: developer-portal-frontend
template:
metadata:
labels:
app: developer-portal-frontend
spec:
containers:
- name: frontend
image: api7/api7-ee-developer-portal-fe:v0.10.0
ports:
- containerPort: 3001
env:
- name: NODE_EXTRA_CA_CERTS
value: /app/apps/site/portal-api-ca.crt
volumeMounts:
- name: config
mountPath: /app/apps/site/config.yaml
subPath: config.yaml
readOnly: true
- name: portal-api-ca
mountPath: /app/apps/site/portal-api-ca.crt
subPath: ca.crt
readOnly: true
volumes:
- name: config
secret:
secretName: developer-portal-frontend-config
- name: portal-api-ca
secret:
secretName: api7-developer-portal-tls
---
apiVersion: v1
kind: Service
metadata:
name: developer-portal-frontend
namespace: api7
spec:
selector:
app: developer-portal-frontend
ports:
- port: 80
targetPort: 3001
type: ClusterIP
❶ Node reads the extra CA file when it starts. This path matches the CA mount below.
❷ Mount the frontend configuration from the config.yaml key in developer-portal-frontend-config.
❸ Mount the ca.crt key into a directory that exists in the frontend image.
❹ The configuration volume references the Secret created in the first document of this manifest.
❺ The CA volume references the TLS Secret created for the Portal API.
Apply the manifest after creating api7-developer-portal-tls above:
kubectl apply -f developer-portal-frontend.yaml
Expose the frontend Service through an Ingress or LoadBalancer so external developers can reach app.baseURL.
The manifest stores the complete frontend configuration in a Kubernetes Secret because it contains the Portal API token, database password, and authentication secret. Kubernetes Secrets are Base64-encoded rather than encrypted by default. Do not commit populated values, restrict Secret access, enable encryption at rest, and use your approved secret-management workflow in production.
Verify the Deployment
- Open the public URL you configured (
app.baseURL) in a browser, for example,https://portal.example.com. - Verify that the home page loads and the Sign Up button is visible.
- Create a test developer account to confirm authentication is functional.
- Navigate to API Hub to confirm the frontend can reach the Portal API. If API products have been published to this portal, they will appear here.
Deploy Multiple Portal Instances
You can deploy multiple Developer Portal frontends against the same backend. Each frontend can serve a different audience, such as public developers, internal teams, or partners:
- In the Provider Portal, create an additional portal with a unique name and public URL.
- Generate a new token for that portal.
- Deploy another frontend instance (a new
Deploymenton Kubernetes or a separate Compose stack) using the new token and public URL in itsconfig.yaml.
API products are published to specific portals. An API product published to one portal is not visible on another unless you explicitly publish it there as well.
Next Steps
- Configure the Developer Portal to set up authentication, public access, and other runtime settings.
- Customize the Developer Portal to apply your branding and extend the frontend.
- Manage API Products to publish APIs for developer consumption.