A REST API for managing Docker containers, built with Quarkus. This service provides endpoints for container lifecycle management including starting, stopping, restarting, and updating containers, as well as log streaming capabilities via Server-Sent Events.
- Features
- Documentation
- Requirements
- Quick Start
- Configuration
- API Reference
- Authentication
- Development
- Testing
- Docker
- Contributing
- License
Detailed technical documentation is available in the docs/ directory:
- Architecture Overview -- System design, component relationships, request processing flows, and deployment architecture with Mermaid diagrams
- API Reference -- Complete REST API specification including endpoint details, request/response schemas, error formats, and authentication requirements
- Container lifecycle management (start, stop, restart, update)
- Container listing with filtering options
- Log retrieval and real-time log streaming via SSE
- Service blacklist for protecting critical containers
- OpenID Connect authentication with ZITADEL support
- Role-based access control
- OpenAPI documentation
- Health checks on dedicated management port
- Multi-architecture Docker images (amd64, arm64)
- Native executable support via GraalVM
- Java 21 or later
- Maven 3.9 or later
- Docker Engine (for container management)
- OIDC Provider (ZITADEL recommended, but any compliant provider works)
- Clone the repository:
git clone https://github.com/AmmannChristian/docker-service-manager.git
cd docker-service-manager-
Configure the OIDC settings (see Configuration section below).
-
Run in development mode:
./mvnw quarkus:devThe API will be available at http://localhost:9080. The Quarkus Dev UI is accessible at http://localhost:9080/q/dev/. Health checks are available on the management port at http://localhost:9090/q/health.
| Variable | Description | Required |
|---|---|---|
OIDC_AUTH_SERVER_URL |
OIDC provider base URL | Yes |
OIDC_CLIENT_ID |
OAuth2 client ID | Yes |
OIDC_JWT_KEY_FILE |
Path to private key file for JWT authentication | Yes |
OIDC_JWT_KEY_ID |
Key ID for JWT signing | Yes |
OIDC_JWT_LIFESPAN |
JWT token lifespan in seconds (default: 300) | No |
OIDC_JWT_AUDIENCE |
JWT audience claim | No |
| Variable | Description | Default |
|---|---|---|
HTTP_HOST |
Bind address | 0.0.0.0 |
HTTP_PORT |
HTTP port | 9080 |
HTTPS_PORT |
HTTPS port | 9443 |
MANAGEMENT_PORT |
Management port (health checks) | 9090 |
HTTP_CORS_ENABLED |
Enable CORS support | true |
HTTP_CORS_ORIGINS |
Allowed CORS origins (regex) | /.*/ |
HTTP_CORS_METHODS |
Allowed HTTP methods | GET,POST,PUT,DELETE,OPTIONS |
HTTP_CORS_HEADERS |
Allowed request headers | accept,authorization,content-type,x-requested-with |
HTTP_CORS_EXPOSED_HEADERS |
Headers exposed to clients | content-disposition |
HTTP2_ENABLED |
Enable HTTP/2 support | true |
| Variable | Description | Default |
|---|---|---|
SERVICE_BLACKLIST |
Comma-separated list of protected containers (by ID, name, or image) | `` |
The blacklist protects critical containers from being stopped, restarted, or updated via the API. Matching is performed against container ID, container name, and image name. Requests to modify blacklisted containers return HTTP 403 Forbidden.
Create a .env file or set environment variables:
export OIDC_AUTH_SERVER_URL=https://your-zitadel-instance.com
export OIDC_CLIENT_ID=your-client-id
export OIDC_JWT_KEY_FILE=/path/to/private-key.json
export OIDC_JWT_KEY_ID=your-key-id
# Optional: Protect critical infrastructure containers
export SERVICE_BLACKLIST=traefik,docker-socket-proxy-ro,docker-socket-proxy-rwThe application requires access to the Docker socket. When running in Docker, mount the socket:
docker run -v /var/run/docker.sock:/var/run/docker.sock your-imageAll endpoints require authentication and the ADMIN_ROLE role.
/api/v1/containers
| Method | Path | Description |
|---|---|---|
| GET | / |
List all containers |
| POST | /{id}/start |
Start a container |
| POST | /{id}/stop |
Stop a container |
| POST | /{id}/restart |
Restart a container |
| POST | /{id}/update |
Pull latest image and recreate container |
| GET | /{id}/logs |
Get container logs |
| GET | /{id}/logs/stream |
Stream container logs via SSE |
curl -X GET "http://localhost:9080/api/v1/containers?all=true" \
-H "Authorization: Bearer <token>"Query parameters:
all(boolean, default: false): Include stopped containers
curl -X POST "http://localhost:9080/api/v1/containers/{containerId}/start" \
-H "Authorization: Bearer <token>"curl -X POST "http://localhost:9080/api/v1/containers/{containerId}/stop" \
-H "Authorization: Bearer <token>"curl -X POST "http://localhost:9080/api/v1/containers/{containerId}/restart" \
-H "Authorization: Bearer <token>"Pulls the latest version of the container image and recreates the container with the same configuration.
curl -X POST "http://localhost:9080/api/v1/containers/{containerId}/update" \
-H "Authorization: Bearer <token>"curl -X GET "http://localhost:9080/api/v1/containers/{containerId}/logs?tail=100" \
-H "Authorization: Bearer <token>"Query parameters:
tail(integer, default: 100, range: 1-10000): Number of log lines to return
curl -X GET "http://localhost:9080/api/v1/containers/{containerId}/logs/stream?follow=true" \
-H "Authorization: Bearer <token>" \
-H "Accept: text/event-stream"Query parameters:
follow(boolean, default: true): Continue streaming new log entries
When running the application, the OpenAPI specification is available at:
- Swagger UI:
http://localhost:9080/q/swagger-ui - OpenAPI JSON:
http://localhost:9080/q/openapi
The application uses OpenID Connect for authentication. It supports both JWT tokens (verified locally) and opaque tokens (verified via introspection).
This application includes a custom security identity augmentor that extracts roles from ZITADEL-specific JWT claims:
urn:zitadel:iam:org:project:roles- Standard ZITADEL roles claimurn:zitadel:iam:org:project:{projectId}:roles- Project-specific roles
The augmentor also falls back to standard claims:
groups- Standard groups claimscope- OAuth2 scopes as roles
All container management endpoints require the ADMIN_ROLE role.
- JDK 21
- Maven 3.9+
- Docker (for running containers)
Development mode enables live coding with automatic restart on code changes:
./mvnw quarkus:devThe project uses Google Java Format via Spotless. To check formatting:
./mvnw spotless:checkTo automatically format code:
./mvnw spotless:applyBuild the application:
./mvnw packageBuild without running tests:
./mvnw package -DskipTestsjava -jar target/quarkus-app/quarkus-run.jar./mvnw test./mvnw verifyCoverage reports are generated in target/site/jacoco/.
The project enforces a minimum line coverage of 90%. The build will fail if coverage falls below this threshold.
The project includes a multi-stage Dockerfile with several targets:
Development image (with hot reload):
docker build --target dev -t docker-service-manager:dev .Production image (JVM mode):
docker build --target prod -t docker-service-manager:latest .Native image (GraalVM):
docker build --target prod-native -t docker-service-manager:native .docker run -d \
--name docker-service-manager \
-p 9080:9080 \
-p 9090:9090 \
-v /var/run/docker.sock:/var/run/docker.sock \
-e OIDC_AUTH_SERVER_URL=https://your-zitadel.com \
-e OIDC_CLIENT_ID=your-client-id \
-e OIDC_JWT_KEY_FILE=/keys/private-key.json \
-e OIDC_JWT_KEY_ID=your-key-id \
-e SERVICE_BLACKLIST=traefik,docker-socket-proxy \
-v /path/to/keys:/keys:ro \
docker-service-manager:latestversion: '3.8'
services:
docker-service-manager:
image: ghcr.io/ammannchristian/docker-service-manager:latest
ports:
- "9080:9080" # API port
- "9090:9090" # Management port (health checks)
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./keys:/keys:ro
environment:
OIDC_AUTH_SERVER_URL: https://your-zitadel.com
OIDC_CLIENT_ID: your-client-id
OIDC_JWT_KEY_FILE: /keys/private-key.json
OIDC_JWT_KEY_ID: your-key-id
SERVICE_BLACKLIST: traefik,docker-socket-proxy # Protect critical containers
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9090/q/health/live"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60sMulti-architecture images are available on GitHub Container Registry:
docker pull ghcr.io/ammannchristian/docker-service-manager:latestAvailable tags:
latest- Latest build from main branchx.y.z- Specific version (e.g.,1.0.0)x.y- Minor version (e.g.,1.0)sha-xxxxxxx- Specific commit
docker-service-manager/
├── src/
│ ├── main/
│ │ ├── java/com/ammann/servicemanager/
│ │ │ ├── config/ # CDI producers and configuration
│ │ │ ├── dto/ # Data transfer objects
│ │ │ ├── health/ # Health check implementations
│ │ │ ├── properties/ # Configuration properties
│ │ │ ├── resource/ # REST endpoints
│ │ │ ├── security/ # Security augmentors
│ │ │ └── service/ # Business logic
│ │ └── resources/
│ │ └── application.properties
│ └── test/
│ └── java/ # Unit and integration tests
├── .github/
│ └── workflows/ # CI/CD pipelines
├── Dockerfile # Multi-stage Docker build
├── pom.xml # Maven configuration
└── README.md
Contributions are welcome. Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/your-feature) - Make your changes
- Ensure tests pass (
./mvnw verify) - Ensure code is formatted (
./mvnw spotless:apply) - Commit your changes
- Push to your branch
- Open a Pull Request
Please ensure your PR:
- Includes tests for new functionality
- Maintains or improves code coverage
- Follows the existing code style
- Updates documentation if needed
This project is licensed under the MIT License. See the LICENSE file for details.