Podman
Deploy Team Server with Podman Compose and a local PostgreSQL container, or run it rootless against an existing PostgreSQL service.
Prerequisites
- Podman 4.4 or later
- A Compose provider for the Compose method
- PostgreSQL 18 or a compatible managed PostgreSQL service
- At least 2 GB of memory and 10 GB of disk for Team Server
- A Team Server license and an OIDC application for production authentication
- A public HTTPS URL reachable by users and Sensors
For rootless services that must survive logout and reboot, enable lingering:
sudo loginctl enable-linger "$USER"Prepare Configuration
Both deployment methods use the same project directory, configuration, and environment file.
mkdir -p team-server/config
cd team-serverSave the production configuration example as config/production.yaml. Before deployment:
- Add the discovery jobs for the CI/CD providers you use.
- Configure at least one OIDC provider.
- Add outbound HTTP policy for private integration endpoints or internal certificate authorities.
- Confirm that
server.host, CORS, andsettings.tenant.base_urluseSERVER_URL.
Generate two distinct JWT secrets and a database password:
openssl rand -base64 48
openssl rand -base64 48
openssl rand -hex 24Create .env:
POSTGRES_DB=team_server
POSTGRES_USER=team_server
POSTGRES_PASSWORD=<generated_database_password>
DATABASE_URL=postgresql://team_server:<generated_database_password>@postgres:5432/team_server
ENDURA_LICENSE_KEY=<license_key>
JWT_SENSOR_SECRET=<first_base64_secret>
JWT_USER_SECRET=<second_base64_secret>
SERVER_URL=https://team-server.example.com
TENANT_NAME=Example Organization
GOOGLE_OIDC_CLIENT_ID=<client_id>
GOOGLE_OIDC_CLIENT_SECRET=<client_secret>Use the variables referenced by your selected OIDC provider, then protect the file:
chmod 600 .env
printf '%s\n' '.env' >> .gitignoreEach JWT secret must decode to at least 32 bytes, and the values must differ.
Method 1: Podman Compose
Create podman-compose.yaml:
services:
postgres:
image: docker.io/postgres:18-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
endura-team-server:
image: ghcr.io/endurasecurity/container/endura-team-server:testing
env_file: .env
ports:
- "5150:5150"
volumes:
- ./config:/app/config:ro,Z
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5150/_readiness"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
restart: unless-stopped
volumes:
postgres_data:Start and verify the services:
podman compose -f podman-compose.yaml up -d
podman compose -f podman-compose.yaml logs -f endura-team-server
podman compose -f podman-compose.yaml ps
curl -f http://localhost:5150/_readinessReadiness returns HTTP 200 with {"ok":true} after startup completes.
Method 2: Existing PostgreSQL
Update DATABASE_URL in .env with the external database host, then run Team Server rootless:
podman run -d \
--name endura-team-server \
--userns=keep-id \
--env-file .env \
-p 5150:5150 \
-v "$PWD/config:/app/config:ro,Z" \
--restart unless-stopped \
ghcr.io/endurasecurity/container/endura-team-server:testingVerify startup:
podman ps --filter name=endura-team-server
podman logs endura-team-server
curl -f http://localhost:5150/_readinessRun Direct Podman with Quadlet
Quadlet gives a rootless direct deployment a persistent user service. Use absolute paths for the environment file and configuration directory.
Create ~/.config/containers/systemd/endura-team-server.container:
[Unit]
Description=Endura Team Server
Wants=network-online.target
After=network-online.target
[Container]
Image=ghcr.io/endurasecurity/container/endura-team-server:testing
ContainerName=endura-team-server
EnvironmentFile=/absolute/path/to/team-server/.env
Volume=/absolute/path/to/team-server/config:/app/config:ro,Z
PublishPort=5150:5150
UserNS=keep-id
[Service]
Restart=always
[Install]
WantedBy=default.targetRemove a manually started container with the same name, then start the service:
podman rm --force endura-team-server
systemctl --user daemon-reload
systemctl --user enable --now endura-team-server.service
systemctl --user status endura-team-server.serviceView service logs with journalctl --user -u endura-team-server.service -f.
Configure HTTPS
Production deployments must expose SERVER_URL over HTTPS. Terminate TLS at a trusted reverse proxy or load balancer, or configure Team Server directly:
settings:
tls:
certificate: "/run/secrets/server_cert"
private_key: "/run/secrets/server_key"Mount the certificate and key into the Team Server container:
-v /absolute/path/server.pem:/run/secrets/server_cert:ro,Z
-v /absolute/path/server-key.pem:/run/secrets/server_key:ro,ZAdd the mounts under volumes for Compose or as Volume= entries for Quadlet. Restart Team Server, then verify:
curl -f https://team-server.example.com/_readinessUse -k only while testing a self-signed certificate.
Set Up the First Administrator
An OIDC user must sign in once before the user record exists. Find the user ID:
podman compose -f podman-compose.yaml exec endura-team-server \
endura task user_get_id email:admin@example.comFor direct Podman, replace the Compose prefix with podman exec endura-team-server.
Assign the Administrator role:
podman compose -f podman-compose.yaml exec endura-team-server \
endura task user_set_role id:42 role:administratorRefresh the browser or sign in again.
Operate the Deployment
Logs and Status
podman compose -f podman-compose.yaml ps
podman compose -f podman-compose.yaml logs -f endura-team-server
podman compose -f podman-compose.yaml logs -f postgresFor Quadlet, use systemctl --user status endura-team-server and journalctl --user -u endura-team-server -f.
Update
Review Runtime Sensor compatibility before a major update.
Podman Compose:
podman compose -f podman-compose.yaml pull endura-team-server
podman compose -f podman-compose.yaml up -d endura-team-serverQuadlet:
podman pull ghcr.io/endurasecurity/container/endura-team-server:testing
systemctl --user restart endura-team-server.serviceBack Up and Restore
podman compose -f podman-compose.yaml exec -T postgres \
sh -c 'pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB"' > team_server.sql
tar -czf team_server_config.tar.gz config/Restore only into an empty or intentionally replaced database:
podman compose -f podman-compose.yaml exec -T postgres \
sh -c 'psql -U "$POSTGRES_USER" "$POSTGRES_DB"' < team_server.sqlUse the database provider’s backup procedure when PostgreSQL is external.
Uninstall
Stop Compose services while preserving their database volume:
podman compose -f podman-compose.yaml downRemove a direct container or Quadlet service:
systemctl --user disable --now endura-team-server.service
podman rm --force endura-team-serverDeleting the Compose Volume Is Permanent
podman compose -f podman-compose.yaml down --volumes deletes PostgreSQL and all Team Server data. Back up the database and confirm the project directory before running it.
Troubleshooting
Container Does Not Start
podman compose -f podman-compose.yaml config
podman compose -f podman-compose.yaml logs endura-team-serverCheck the configuration mount, referenced environment variables, and the two JWT secrets.
Database Connection Fails
podman compose -f podman-compose.yaml logs postgres
podman compose -f podman-compose.yaml exec postgres \
sh -c 'pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"'Compose uses the database host postgres. For an external database, confirm DNS, port 5432, TLS, and credentials from the Team Server container.
Rootless Service Stops After Logout
loginctl show-user "$USER" -p Linger
systemctl --user status endura-team-server.serviceEnable lingering and use Quadlet for a persistent rootless direct deployment.
SELinux Denies a Mount
Use :Z on private bind mounts and review denials with ausearch -m AVC -ts recent.
Internal Integration URL Is Blocked
Configure settings.http.allowed_networks and, when needed, settings.http.ca_certificates. See Outbound HTTP Policy.
For additional help, contact support@endurasecurity.com.