Skip to main content
This page provides a comprehensive reference of all environment variables used by the ZKTeco Biometric Server implementations.

Overview

The server supports configuration through environment variables, allowing you to customize behavior without modifying source code. Different server implementations use different subsets of these variables.
All environment variables are optional and have sensible defaults.

Quick Reference Table

VariableDefaultservidor.pyserver.pyservidor_multi.pyDescription
ZK_IP192.168.1.205Single device IP address
ZK_PORT4370Single device port
ZK_PASSWORD0Single device password
ZK_TIMEOUT5Single device timeout (seconds)
API_HOST0.0.0.0Server listening interface
API_PORT5000Server listening port
CERT_FILEcert.pemSSL certificate file path
KEY_FILEkey.pemSSL private key file path
DEVICES_FILEdevices.jsonDevice registry file path
SERVER_HOST192.168.1.100Server’s public/internal IP
PORT5000Alternative port variable (Render)

Device Connection Variables

ZK_IP

ZK_IP
string
default:"192.168.1.205"
IP address of the ZKTeco biometric device.Used by: servidor.py (single-device only)Example:
export ZK_IP="192.168.1.210"
Notes:
  • Must be a valid IPv4 address
  • Device must be reachable on the network
  • For multi-device servers, devices are registered via API instead

ZK_PORT

ZK_PORT
integer
default:"4370"
Port number of the ZKTeco device.Used by: servidor.py (single-device only)Example:
export ZK_PORT="4370"
Notes:
  • Default ZKTeco port is 4370
  • Some devices may use custom ports
  • Verify device configuration if connection fails

ZK_PASSWORD

ZK_PASSWORD
integer
default:"0"
Communication password for the ZKTeco device.Used by: servidor.py (single-device only)Example:
export ZK_PASSWORD="0"
Notes:
  • Most devices use default password 0
  • Check device manual if connection requires password
  • Must be numeric value

ZK_TIMEOUT

ZK_TIMEOUT
integer
default:"5"
Connection timeout in seconds for device communication.Used by: servidor.py (single-device only)Example:
export ZK_TIMEOUT="10"
Notes:
  • Increase for slow networks or distant devices
  • Values too low may cause premature timeouts
  • Recommended range: 5-30 seconds

Server Configuration Variables

API_HOST

API_HOST
string
default:"0.0.0.0"
Network interface the Flask server listens on.Used by: All server implementationsExample:
# Listen on all interfaces
export API_HOST="0.0.0.0"

# Listen only on localhost
export API_HOST="127.0.0.1"

# Listen on specific interface
export API_HOST="192.168.1.100"
Common values:
  • 0.0.0.0 - Listen on all network interfaces (default, recommended)
  • 127.0.0.1 - Listen only on localhost (local access only)
  • Specific IP - Listen on single interface

API_PORT

API_PORT
integer
default:"5000"
Port number the Flask server listens on.Used by: All server implementationsExample:
export API_PORT="8080"
Notes:
  • Must not conflict with other services
  • Ports below 1024 require root privileges on Linux
  • Common alternatives: 5000, 8000, 8080, 3000

PORT

PORT
integer
default:"5000"
Alternative port variable, primarily for cloud platforms like Render.Used by: servidor_multi.py onlyExample:
export PORT="10000"
Notes:
  • Takes precedence over API_PORT in servidor_multi.py
  • Used by Render and some other cloud platforms
  • If both PORT and API_PORT are set, PORT is used
Source code reference:
port = int(os.getenv("PORT", 5000))
app.run(host="0.0.0.0", port=port, debug=False)

SERVER_HOST

SERVER_HOST
string
default:"192.168.1.100"
The server’s public or internal IP address for display and certificate CN.Used by: servidor_multi.py onlyExample:
export SERVER_HOST="biometric.example.com"
export SERVER_HOST="10.0.0.50"
Usage:
  • Used in health check response to show access URL
  • Sets Common Name (CN) when generating SSL certificates
  • Purely informational for API responses
Source code reference:
SERVER_HOST = os.getenv("SERVER_HOST", "192.168.1.100")
# ...
cert.get_subject().CN = SERVER_HOST

SSL/TLS Configuration Variables

CERT_FILE

CERT_FILE
string
default:"cert.pem"
Path to SSL certificate file (PEM format).Used by: All server implementationsExample:
# Relative path (default)
export CERT_FILE="cert.pem"

# Absolute path (production)
export CERT_FILE="/etc/ssl/certs/zkteco.crt"

# Let's Encrypt
export CERT_FILE="/etc/letsencrypt/live/biometric.example.com/fullchain.pem"
Notes:
  • File must exist and be readable
  • Can be relative or absolute path
  • Used together with KEY_FILE for HTTPS
  • If file doesn’t exist, server attempts auto-generation

KEY_FILE

KEY_FILE
string
default:"key.pem"
Path to SSL private key file (PEM format).Used by: All server implementationsExample:
# Relative path (default)
export KEY_FILE="key.pem"

# Absolute path (production)
export KEY_FILE="/etc/ssl/private/zkteco.key"

# Let's Encrypt
export KEY_FILE="/etc/letsencrypt/live/biometric.example.com/privkey.pem"
Notes:
  • File must exist and be readable
  • Should have restrictive permissions (600 or 640)
  • Never commit private keys to version control
  • If file doesn’t exist, server attempts auto-generation

Data Persistence Variables

DEVICES_FILE

DEVICES_FILE
string
default:"devices.json"
Path to JSON file storing device registry for multi-device servers.Used by: server.py and servidor_multi.pyExample:
# Relative path (default)
export DEVICES_FILE="devices.json"

# Absolute path
export DEVICES_FILE="/var/lib/zkteco/devices.json"

# Docker volume
export DEVICES_FILE="/data/devices.json"
File format:
{
  "device_id": {
    "ip": "192.168.1.205",
    "port": 4370,
    "password": 0,
    "timeout": 5,
    "name": "Device Name",
    "created_at": "2026-03-06 10:30:00",
    "updated_at": "2026-03-06 14:20:00"
  }
}
Behavior:
  • Created automatically on first run if missing
  • Updated automatically on device add/edit/delete
  • Loaded on server startup to restore device registry
  • Must be writable by server process

Configuration by Server Type

servidor.py (Single Device)

For managing one ZKTeco device:
# Device connection (required)
export ZK_IP="192.168.1.205"
export ZK_PORT="4370"
export ZK_PASSWORD="0"
export ZK_TIMEOUT="5"

# Server configuration
export API_HOST="0.0.0.0"
export API_PORT="5000"

# SSL configuration (optional)
export CERT_FILE="cert.pem"
export KEY_FILE="key.pem"

# Start server
python servidor.py

server.py (Multi-Device)

For managing multiple devices via API:
# Server configuration
export API_HOST="0.0.0.0"
export API_PORT="5000"

# Device persistence
export DEVICES_FILE="devices.json"

# SSL configuration (optional)
export CERT_FILE="cert.pem"
export KEY_FILE="key.pem"

# Start server
python server.py
Devices are registered via POST /devices endpoint, not environment variables.

servidor_multi.py (Multi-Device for Render)

For cloud deployment with optional static device configuration:
# Server configuration
export API_HOST="0.0.0.0"
export SERVER_HOST="biometric.example.com"
export PORT="10000"  # Or API_PORT="10000"

# Device persistence
export DEVICES_FILE="/data/devices.json"

# SSL configuration (optional, may not be needed with platform SSL)
export CERT_FILE="cert.pem"
export KEY_FILE="key.pem"

# Start server
python servidor_multi.py

Deployment Examples

Local Development

export ZK_IP="192.168.1.205"
export API_PORT="5000"
python servidor.py

Systemd Service

Create /etc/systemd/system/zkteco.service:
[Unit]
Description=ZKTeco Biometric Server
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/zkteco

# Single device configuration
Environment="ZK_IP=192.168.1.205"
Environment="ZK_PORT=4370"
Environment="ZK_TIMEOUT=5"

# Server configuration
Environment="API_HOST=0.0.0.0"
Environment="API_PORT=5000"

# SSL configuration
Environment="CERT_FILE=/etc/letsencrypt/live/biometric.example.com/fullchain.pem"
Environment="KEY_FILE=/etc/letsencrypt/live/biometric.example.com/privkey.pem"

ExecStart=/usr/bin/python3 servidor.py
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
Or use an environment file:
[Service]
EnvironmentFile=/etc/zkteco/server.env
ExecStart=/usr/bin/python3 servidor.py

Docker

FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY servidor.py .

ENV ZK_IP=192.168.1.205
ENV ZK_PORT=4370
ENV API_HOST=0.0.0.0
ENV API_PORT=5000

EXPOSE 5000
CMD ["python", "servidor.py"]
Run:
docker build -t zkteco-single .
docker run -p 5000:5000 \
  -e ZK_IP="192.168.1.205" \
  -e ZK_PORT="4370" \
  zkteco-single

Docker Compose

version: '3.8'

services:
  zkteco-single:
    build: .
    image: zkteco-single
    ports:
      - "5000:5000"
    environment:
      ZK_IP: "192.168.1.205"
      ZK_PORT: "4370"
      ZK_PASSWORD: "0"
      ZK_TIMEOUT: "5"
      API_HOST: "0.0.0.0"
      API_PORT: "5000"
    restart: unless-stopped
    network_mode: host

  zkteco-multi:
    build: .
    image: zkteco-multi
    ports:
      - "5001:5000"
    environment:
      API_HOST: "0.0.0.0"
      API_PORT: "5000"
      DEVICES_FILE: "/data/devices.json"
    volumes:
      - ./data:/data
    restart: unless-stopped
    network_mode: host

Kubernetes

apiVersion: v1
kind: ConfigMap
metadata:
  name: zkteco-config
data:
  ZK_IP: "192.168.1.205"
  ZK_PORT: "4370"
  API_HOST: "0.0.0.0"
  API_PORT: "5000"
---
apiVersion: v1
kind: Secret
metadata:
  name: zkteco-ssl
type: Opaque
data:
  cert.pem: <base64-encoded-cert>
  key.pem: <base64-encoded-key>
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: zkteco-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: zkteco
  template:
    metadata:
      labels:
        app: zkteco
    spec:
      containers:
      - name: zkteco
        image: zkteco-server:latest
        ports:
        - containerPort: 5000
        envFrom:
        - configMapRef:
            name: zkteco-config
        volumeMounts:
        - name: ssl-certs
          mountPath: /app/certs
          readOnly: true
        env:
        - name: CERT_FILE
          value: "/app/certs/cert.pem"
        - name: KEY_FILE
          value: "/app/certs/key.pem"
      volumes:
      - name: ssl-certs
        secret:
          secretName: zkteco-ssl

Cloud Platforms

In Render dashboard, set environment variables:
PORT=10000
SERVER_HOST=your-service.onrender.com
DEVICES_FILE=/opt/render/project/data/devices.json
Render provides HTTPS automatically, so SSL variables are not needed.

Environment Variable Precedence

When multiple configuration sources exist:
  1. Explicit environment variables (highest priority)
  2. Environment files (e.g., systemd EnvironmentFile)
  3. Default values in source code (lowest priority)
Example:
# Default in code: API_PORT = 5000
# Environment file: API_PORT=8080
# Explicit export: export API_PORT=9000

# Result: Server listens on port 9000

Validation and Debugging

Check Current Environment

# View all environment variables
env | grep -E "(ZK_|API_|CERT_|KEY_|DEVICES_|SERVER_|PORT)"

# Check specific variable
echo $ZK_IP
echo $API_PORT

Verify Server Configuration

The server logs show active configuration on startup:
2026-03-06 10:30:00 [INFO] Servidor ZKTeco en https://0.0.0.0:5000
2026-03-06 10:30:00 [INFO] Biometrico: 192.168.1.205:4370

Common Issues

Problem: Changed environment variable but server uses old valueSolutions:
  • Restart the server after changing variables
  • For systemd: sudo systemctl daemon-reload && sudo systemctl restart zkteco
  • Verify variable is actually set: env | grep VAR_NAME
  • Check for typos in variable names (case-sensitive)
Problem: ValueError: invalid literal for int()Solutions:
  • Ensure numeric variables contain only digits:
    • export ZK_PORT="4370"
    • export ZK_PORT="4370 " (trailing space)
    • export ZK_PORT="port4370" (contains text)
Problem: FileNotFoundError for CERT_FILE, KEY_FILE, or DEVICES_FILESolutions:
  • Use absolute paths for production: /etc/ssl/certs/cert.pem
  • Verify file exists: ls -la $CERT_FILE
  • Check permissions: stat $CERT_FILE
  • Ensure working directory is correct
Problem: OSError: [Errno 98] Address already in useSolutions:
  • Check what’s using the port: sudo lsof -i :5000
  • Change API_PORT to unused port
  • Kill conflicting process: sudo kill <PID>

Security Considerations

Never commit environment files containing sensitive data to version control.

Sensitive Variables

These variables may contain sensitive information:
  • ZK_PASSWORD - Device access password
  • KEY_FILE path - Location of private key
  • DEVICES_FILE path - May reveal infrastructure details

Best Practices

Use Secret Management

For production, use proper secret management:
  • AWS Secrets Manager
  • HashiCorp Vault
  • Kubernetes Secrets
  • Environment variable injection from CI/CD

Restrict File Permissions

Protect environment files:
chmod 600 /etc/zkteco/server.env
chown root:root /etc/zkteco/server.env

Use .gitignore

Exclude sensitive files:
.env
*.env
devices.json
*.pem
*.key
*.crt

Rotate Certificates

Regularly update SSL certificates:
# Automated with Let's Encrypt
certbot renew --deploy-hook "systemctl restart zkteco"

Next Steps

Single Device Deployment

Deploy servidor.py with environment variables

Multi-Device Deployment

Deploy server.py with devices.json persistence

SSL Configuration

Configure HTTPS with certificates

API Reference

Use the configured server

Build docs developers (and LLMs) love