Podman API
In reference to: https://github.com/edurange/demo-podman-api-extra
More design thoughts and justifications to be added later, these are just the protocol specs.
This design is not meant to be followed, it's just my first sketch at listing everything we might need.
Testing instructions can be found here, or run the demo script.
Overview
- Container lifecycle management (create, start, stop, remove)
- Container listing and monitoring
- Log retrieval
- User management within containers
- File operations
- Command execution
- Health checks and metrics
Prerequisites
Python 3.7+
Podman installed and configured
Required Python packages:
pip install flask flask-cors podman
Running the API
python3 demo-podman-api.py
The API will start on http://localhost:5000 by default.
Configuration
Configure the API using environment variables:
Example:
export MAX_WORKERS=20 export DEBUG=true export REQUEST_TIMEOUT=60 python3 demo-podman-api.py
API Endpoints
Base URL
All endpoints are prefixed with /api/v1
Container Management
Create Container
POST /containers
Creates and starts a new container.
Request Body:
{
"image": "alpine:latest",
"name": "my-container",
"command": "sleep 300",
"environment": {"ENV_VAR": "value"},
"ports": {"8080": "80"},
"volumes": {"/host/path": "/container/path"},
"user": "root"
}
Required Fields: image, name
Response:
{
"success": true,
"container": {
"id": "abc123def456",
"name": "my-container",
"status": "running"
}
}
List Containers
GET /containers
Lists all containers (running and stopped).
Response:
{
"success": true,
"containers": [
{
"id": "abc123def456",
"name": "my-container",
"status": "running",
"image": "alpine:latest"
}
]
}
Start Container
POST /containers/{name}/start
Starts a stopped container.
Response:
{
"success": true,
"container": {
"name": "my-container",
"status": "started"
}
}
Stop Container
POST /containers/{name}/stop
Stops a running container.
Response:
{
"success": true,
"container": {
"name": "my-container",
"status": "stopped"
}
}
Remove Container
DELETE /containers/{name}
Removes a container.
Query Parameters: - force (boolean): Force removal of running container
Response:
{
"success": true,
"container": {
"name": "my-container",
"removed": true
}
}
Get Container Logs
GET /containers/{name}/logs
Retrieves container logs.
Query Parameters: - tail (integer): Number of lines to retrieve (default: 100)
Response:
{
"success": true,
"container": {
"logs": "Container log output here..."
}
}
Container Operations
Execute Command
POST /containers/{name}/exec
Executes a command inside a container.
Request Body:
{
"command": "ls -la /tmp",
"user": "root"
}
Required Fields: command
Response:
{
"success": true,
"execution": {
"exit_code": 0,
"output": "total 4\ndrwxrwxrwt 2 root root 4096 Jan 1 00:00 .\n",
"success": true
}
}
Add User
POST /containers/{name}/users
Creates a new user inside a container.
Request Body:
{
"username": "newuser",
"password": "password123",
"shell": "/bin/bash"
}
Required Fields: username
Response:
{
"success": true,
"user": {
"username": "newuser",
"created": true
}
}
Add File
POST /containers/{name}/files
Creates a file inside a container.
Request Body:
{
"dest_path": "/tmp/myfile.txt",
"content": "Hello, World!\nThis is file content."
}
Required Fields: dest_path, content
Response:
{
"success": true,
"file": {
"dest_path": "/tmp/myfile.txt",
"size": 32
}
}
System Endpoints
Health Check
GET /health
Checks API and Podman connectivity.
Response:
{
"status": "healthy",
"podman": "connected",
"timestamp": "2025-01-19T15:47:19.123456"
}
Metrics
GET /metrics
Returns API performance metrics.
Response:
{
"uptime_seconds": 3600,
"uptime_human": "1:00:00",
"active_threads": 2,
"max_workers": 10,
"timestamp": "2025-01-19T15:47:19.123456"
}
Error Handling
Error Response Format
All errors follow a consistent format:
{
"success": false,
"error": {
"message": "Error description",
"code": 404,
"type": "PodmanAPIError"
},
"timestamp": "2025-01-19T15:47:19.123456"
}
HTTP Status Codes
| Code | Description |
|---|---|
| 200 | Success |
| 400 | Bad Request (missing required fields) |
| 404 | Resource not found (container, image, etc.) |
| 408 | Request timeout |
| 500 | Internal server error |
| 503 | Service unavailable (Podman connection issues) |
Common Error Types
- PodmanAPIError: Podman-related errors
- InternalError: Server-side errors
- ValidationError: Request validation failures
Examples
Complete Container Workflow
# 1. Check API health
curl -X GET http://localhost:5000/api/v1/health | jq
# 2. Create and start container
curl -X POST http://localhost:5000/api/v1/containers \
-H "Content-Type: application/json" \
-d '{
"image": "alpine:latest",
"name": "demo-container",
"command": "sleep 300"
}' | jq
# 3. Add a user
curl -X POST http://localhost:5000/api/v1/containers/demo-container/users \
-H "Content-Type: application/json" \
-d '{
"username": "testuser",
"password": "testpass",
"shell": "/bin/sh"
}' | jq
# 4. Create a file
curl -X POST http://localhost:5000/api/v1/containers/demo-container/files \
-H "Content-Type: application/json" \
-d '{
"dest_path": "/tmp/demo.txt",
"content": "Hello from the API!"
}' | jq
# 5. Execute commands
curl -X POST http://localhost:5000/api/v1/containers/demo-container/exec \
-H "Content-Type: application/json" \
-d '{"command": "cat /tmp/demo.txt"}' | jq
# 6. Check logs
curl -X GET "http://localhost:5000/api/v1/containers/demo-container/logs?tail=20" | jq
# 7. Stop and remove container
curl -X POST http://localhost:5000/api/v1/containers/demo-container/stop | jq
curl -X DELETE "http://localhost:5000/api/v1/containers/demo-container?force=true" | jq
Python Client Example
import requests
import json
class PodmanAPIClient:
def __init__(self, base_url="http://localhost:5000/api/v1"):
self.base_url = base_url
def create_container(self, image, name, **kwargs):
data = {"image": image, "name": name, **kwargs}
response = requests.post(f"{self.base_url}/containers", json=data)
return response.json()
def execute_command(self, container_name, command, user=None):
data = {"command": command}
if user:
data["user"] = user
response = requests.post(
f"{self.base_url}/containers/{container_name}/exec",
json=data
)
return response.json()
# Usage
client = PodmanAPIClient()
result = client.create_container("alpine:latest", "test-container")
print(json.dumps(result, indent=2))
Security Considerations
- The API runs without authentication by default
- Container commands are executed with full privileges
- File operations can write anywhere in the container filesystem
- Consider implementing authentication and authorization for production use
- Validate and sanitize all user inputs
- Consider running the API behind a reverse proxy with rate limiting
Troubleshooting
Common Issues
- “Container not found” errors:
Ensure the container name is correct and the container exists
- “Image not found” errors:
Pull the image with podman pull <image> first
- Permission errors:
Ensure Podman is properly configured for your user
- Connection errors:
Check that Podman service is running
Debug Mode
Enable debug mode for detailed logging:
export DEBUG=true python3 demo-podman-api.py
Logs
The API logs all operations. Check the console output for detailed error information.