Hanzo

Deploying with Docker

Run Hanzo IAM with Docker or Docker Compose and optional reverse proxies.

This page describes deploying Hanzo IAM with Docker or Docker Compose, with optional reverse proxy (Traefik, Nginx, Caddy) configs.

Prerequisites

  • Docker
  • Docker Compose (for the compose method)
  • A domain pointing to your server (for reverse proxy setups)

Deployment options


<Tabs
  defaultValue="docker-compose"
  values={[
    { label: 'Docker Compose', value: 'docker-compose', },
    { label: 'Docker Run', value: 'docker-run', },
  ]
}>

<TabItem value="docker-compose">

### Using Docker Compose

Create a `docker-compose.yml` file:

```yaml
services:
  iam:
    image: casbin/iam:latest
    container_name: iam
    restart: unless-stopped
    ports:
      - "8000:8000"
    environment:
      - GIN_MODE=release
    volumes:
      - ./conf:/conf
      - ./logs:/logs
    networks:
      - iam-network

networks:
  iam-network:
    driver: bridge

Start the service:

```bash
docker-compose up -d

</TabItem>

<TabItem value="docker-run">

### Using Docker Run

Run Hanzo IAM directly with Docker:

```bash
docker run -d \
  --name iam \
  --restart unless-stopped \
  -p 8000:8000 \
  -v $(pwd)/conf:/conf \
  -v $(pwd)/logs:/logs \
  -e GIN_MODE=release \
  casbin/iam:latest

</TabItem>

</Tabs>

Reverse Proxy Configuration

For production deployments, it's recommended to use a reverse proxy with TLS certificates. Choose your preferred reverse proxy:


<Tabs
  defaultValue="traefik-labels"
  values={[
    { label: 'Traefik (Labels)', value: 'traefik-labels', },
    { label: 'Traefik (Dynamic)', value: 'traefik-dynamic', },
    { label: 'Nginx', value: 'nginx', },
    { label: 'Caddy', value: 'caddy', },
  ]
}>

<TabItem value="traefik-labels">

### Traefik with Docker Labels

Create a `docker-compose.yml` with Traefik labels:

```yaml
services:
  traefik:
    image: traefik:v2.10
    container_name: traefik
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik/acme.json:/acme.json
    command:
      - --api.dashboard=true
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --certificatesresolvers.letsencrypt.acme.email=your-email@example.com
      - --certificatesresolvers.letsencrypt.acme.storage=/acme.json
      - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
    networks:
      - iam-network

  iam:
    image: casbin/iam:latest
    container_name: iam
    restart: unless-stopped
    environment:
      - GIN_MODE=release
    volumes:
      - ./conf:/conf
      - ./logs:/logs
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.iam.rule=Host(`your-domain.com`)"
      - "traefik.http.routers.iam.entrypoints=websecure"
      - "traefik.http.routers.iam.tls.certresolver=letsencrypt"
      - "traefik.http.services.iam.loadbalancer.server.port=8000"
    networks:
      - iam-network

networks:
  iam-network:
    driver: bridge

Create the acme.json file:

```bash
touch traefik/acme.json
chmod 600 traefik/acme.json

</TabItem>

<TabItem value="traefik-dynamic">

### Traefik with Dynamic Configuration

Create a `docker-compose.yml` with dynamic configuration:

```yaml
services:
  traefik:
    image: traefik:v2.10
    container_name: traefik
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik/acme.json:/acme.json
      - ./traefik/traefik.yml:/etc/traefik/traefik.yml
      - ./traefik/dynamic.yml:/etc/traefik/dynamic.yml
    command:
      - --configfile=/etc/traefik/traefik.yml
    networks:
      - iam-network

  iam:
    image: casbin/iam:latest
    container_name: iam
    restart: unless-stopped
    environment:
      - GIN_MODE=release
    volumes:
      - ./conf:/conf
      - ./logs:/logs
    networks:
      - iam-network

networks:
  iam-network:
    driver: bridge

Create `traefik/traefik.yml`:

```yaml
api:
  dashboard: true

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entrypoint:
          to: websecure
          scheme: https
  websecure:
    address: ":443"

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
  file:
    directory: /etc/traefik
    watch: true

certificatesResolvers:
  letsencrypt:
    acme:
      email: your-email@example.com
      storage: /acme.json
      httpChallenge:
        entryPoint: web

Create `traefik/dynamic.yml`:

```yaml
http:
  routers:
    iam:
      rule: "Host(`your-domain.com`)"
      service: iam
      tls:
        certResolver: letsencrypt
      entryPoints:
        - websecure

  services:
    iam:
      loadBalancer:
        servers:
          - url: "http://iam:8000"

</TabItem>

<TabItem value="nginx">

### Nginx with Let's Encrypt

Create a `docker-compose.yml` with Nginx:

```yaml
services:
  nginx:
    image: nginx:alpine
    container_name: nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./certbot/conf:/etc/letsencrypt
      - ./certbot/www:/var/www/certbot
    depends_on:
      - iam
    networks:
      - iam-network

  certbot:
    image: certbot/certbot
    container_name: certbot
    volumes:
      - ./certbot/conf:/etc/letsencrypt
      - ./certbot/www:/var/www/certbot
    command: certonly --webroot --webroot-path=/var/www/certbot --email your-email@example.com --agree-tos --no-eff-email -d your-domain.com

  iam:
    image: casbin/iam:latest
    container_name: iam
    restart: unless-stopped
    environment:
      - GIN_MODE=release
    volumes:
      - ./conf:/conf
      - ./logs:/logs
    networks:
      - iam-network

networks:
  iam-network:
    driver: bridge

Create `nginx/conf.d/default.conf`:

```nginx
server {
    listen 80;
    server_name your-domain.com;
    
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }
    
    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name your-domain.com;
    
    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
    
    location / {
        proxy_pass http://iam:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Initialize SSL certificates:

```bash
# Create directories
mkdir -p certbot/conf certbot/www nginx/conf.d

# Get initial certificate
docker-compose run --rm certbot

# Add to crontab for renewal
echo "0 12 * * * docker-compose run --rm certbot renew" | crontab -

</TabItem>

<TabItem value="caddy">

### Caddy with Automatic HTTPS

Create a `docker-compose.yml` with Caddy:

```yaml
services:
  caddy:
    image: caddy:2-alpine
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./caddy/Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - iam
    networks:
      - iam-network

  iam:
    image: casbin/iam:latest
    container_name: iam
    restart: unless-stopped
    environment:
      - GIN_MODE=release
    volumes:
      - ./conf:/conf
      - ./logs:/logs
    networks:
      - iam-network

volumes:
  caddy_data:
  caddy_config:

networks:
  iam-network:
    driver: bridge

Create `caddy/Caddyfile`:

```caddyfile
your-domain.com {
    reverse_proxy iam:8000 {
        header_up Host {host}
        header_up X-Real-IP {remote}
        header_up X-Forwarded-For {remote}
        header_up X-Forwarded-Proto {scheme}
    }
}

</TabItem>

</Tabs>

Configuration

Create the necessary configuration directories

mkdir -p conf logs

### Download the Hanzo IAM configuration files

```bash
wget https://raw.githubusercontent.com/iam/iam/master/conf/app.conf -O conf/app.conf
wget https://raw.githubusercontent.com/iam/iam/master/init_data.json.template -O conf/init_data.json

### Edit `conf/app.conf` to match your environment settings

:::note
The default user of Hanzo IAM has a uid and gid of 1000. When using volume mapping with SQLite (or any storage requiring file permissions), ensure that the path (e.g., `/folder/of/app.conf` in the example above) is accessible to uid 1000. This avoids permission errors like `permission denied` when Hanzo IAM writes to mapped volumes.
:::

## Testing

After deployment, visit your domain in your browser:

- **Docker Run/Compose only**: `http://your-server-ip:8000`
- **With Reverse Proxy**: `https://your-domain.com`

## Troubleshooting

### Check container logs

```bash
# For docker-compose
docker-compose logs iam

# For docker run
docker logs iam

### Verify reverse proxy configuration

```bash
# Check if containers are running
docker ps

# Test connectivity
curl -I http://localhost:8000

### SSL Certificate Issues

- Ensure your domain points to the correct server IP
- Check that ports 80 and 443 are open in your firewall
- Verify DNS propagation with `nslookup your-domain.com`

How is this guide?

Last updated on

On this page