Technology Aug 26, 2026 · 9 min read

Deploying ClearML as an Azure ML Alternative

Azure Machine Learning ties experiment tracking, pipelines, and model serving to Azure-specific APIs and managed-compute pricing. ClearML is an open-source MLOps platform that provides the same capabilities — self-hosted, on any infrastructure. This guide deploys ClearML Server with Docker Compose a...

DE
DEV Community
by Sanskriti Harmukh
Deploying ClearML as an Azure ML Alternative

Azure Machine Learning ties experiment tracking, pipelines, and model serving to Azure-specific APIs and managed-compute pricing. ClearML is an open-source MLOps platform that provides the same capabilities — self-hosted, on any infrastructure. This guide deploys ClearML Server with Docker Compose and Traefik, configures agents for remote execution, tracks an experiment, builds a pipeline, runs hyperparameter optimization, and serves a model with Triton.

Prerequisites: a Linux server, non-root sudo user, Docker + Docker Compose, DNS A records for app.clearml.example.com, api.clearml.example.com, files.clearml.example.com. GPU workloads (optional) need the NVIDIA Container Toolkit on the agent host.

Architecture

Azure ML ClearML equivalent
Azure ML Studio ClearML Web UI
Azure ML Experiments Experiment Manager (auto-tracking)
Azure ML Jobs Agent + Tasks
Azure ML Pipelines ClearML Pipelines (Python DAG)
Azure ML Model Registry Model Repository
Azure ML Endpoints ClearML Serving (Triton)

Server components: API server, web UI, file server — backed by MongoDB + Elasticsearch. Agents are worker daemons that pull tasks from queues and run them on any machine with Python.

Deploy ClearML Server

$ echo "vm.max_map_count=524288" | sudo tee /etc/sysctl.d/99-clearml.conf
$ sudo sysctl --system
$ sudo systemctl restart docker
$ sudo mkdir -p /opt/clearml/{data/elastic_7,data/mongo_4/db,data/mongo_4/configdb,data/redis,data/fileserver,logs,config}
$ sudo chown -R 1000:1000 /opt/clearml
$ mkdir -p ~/clearml && cd ~/clearml
$ curl -fsSL https://raw.githubusercontent.com/clearml/clearml-server/master/docker/docker-compose.yml -o docker-compose.yml

Edit docker-compose.yml: comment out every ports: block under apiserver, webserver, fileserver (Traefik handles routing), and set named bridge networks:

networks:
  backend:
    name: clearml_backend
    driver: bridge
  frontend:
    name: clearml_frontend
    driver: bridge

Create .env (replace clearml.example.com with your domain):

CLEARML_WEB_HOST=https://app.clearml.example.com
CLEARML_API_HOST=https://api.clearml.example.com
CLEARML_FILES_HOST=https://files.clearml.example.com
$ docker compose up -d
$ docker compose ps
$ docker compose logs --tail 50

Configure Traefik

$ mkdir -p ~/clearml/traefik && cd ~/clearml/traefik
$ mkdir -p letsencrypt && touch letsencrypt/acme.json
$ chmod 600 letsencrypt/acme.json

.env (replace with your email):

LETSENCRYPT_EMAIL=admin@example.com

docker-compose.yml:

services:
  traefik:
    image: traefik:v3.6
    container_name: traefik
    command:
      - "--log.level=INFO"
      - "--providers.file.filename=/etc/traefik/dynamic_conf.yml"
      - "--entryPoints.web.address=:80"
      - "--entryPoints.websecure.address=:443"
      - "--entryPoints.web.http.redirections.entrypoint.to=websecure"
      - "--certificatesResolvers.le.acme.httpChallenge.entryPoint=web"
      - "--certificatesResolvers.le.acme.email=${LETSENCRYPT_EMAIL}"
      - "--certificatesResolvers.le.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "./dynamic_conf.yml:/etc/traefik/dynamic_conf.yml:ro"
    networks:
      - clearml-frontend
    restart: unless-stopped

networks:
  clearml-frontend:
    name: clearml_frontend
    external: true

dynamic_conf.yml routes each subdomain to its container (clearml-webserver:80, clearml-apiserver:8008, clearml-fileserver:8081) with certResolver: le. Full rules in the source repo.

$ docker compose up -d
$ docker logs traefik 2>&1 | grep -i certificate

Configure ClearML Server

  1. Visit https://app.clearml.example.com, create the admin account (username + company name).
  2. Settings → Workspace → Create new credentials.
  3. Save the credentials block — needed for agents and the SDK:
api {
  web_server: https://app.clearml.example.com
  api_server: https://api.clearml.example.com
  files_server: https://files.clearml.example.com
  credentials {
    "access_key" = "YOUR_ACCESS_KEY"
    "secret_key" = "YOUR_SECRET_KEY"
  }
}

Deploy an Agent

Agents can run on the server itself or a dedicated (ideally GPU-enabled) machine.

$ mkdir -p ~/clearml-agent && cd ~/clearml-agent
$ sudo apt install python3.12-venv -y
$ python3 -m venv clearml_venv
$ source clearml_venv/bin/activate
$ pip install clearml-agent
$ clearml-agent init

Paste the credentials block when prompted, accept defaults for the rest. Then start it:

$ clearml-agent daemon --queue default --detached

GPU workloads:

$ clearml-agent daemon --gpus 0,1 --queue default --detached

Confirm it registered under Workers & Queues → Workers in the web UI.

Install the SDK

$ source ~/clearml-agent/clearml_venv/bin/activate
$ pip install clearml scikit-learn joblib pandas
$ clearml-init

Paste the credentials block again when prompted — saves to ~/clearml.conf.

Track an Experiment

$ mkdir -p ~/clearml/experiments && cd ~/clearml/experiments
$ nano 01_first_experiment.py
import joblib
from clearml import Task
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score

task = Task.init(project_name='ClearML Tutorial', task_name='01_First_Experiment', tags=['tutorial'])

hyperparams = {'n_estimators': 100, 'max_depth': 5, 'random_state': 42}
task.connect(hyperparams)

iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=42)

clf = RandomForestClassifier(**hyperparams)
clf.fit(X_train, y_train)

accuracy = accuracy_score(y_test, clf.predict(X_test))
task.get_logger().report_scalar(title='Performance', series='Accuracy', value=accuracy, iteration=1)

joblib.dump(clf, 'iris_rf_model.pkl')
task.upload_artifact(name='trained_model', artifact_object='iris_rf_model.pkl')
task.close()
$ python3 01_first_experiment.py

Task.init auto-captures code, environment, and hyperparameters — no manual logging needed beyond report_scalar. Open the printed task URL to see it in the web UI: Execution, Configuration, Artifacts, Console, Scalars, Plots tabs.

Build a Pipeline

clearml.PipelineController chains functions into a DAG; step outputs feed downstream steps automatically:

from clearml import PipelineController

def step_one(pickle_data_url):
    import pickle, pandas as pd
    from clearml import StorageManager
    local_pkl = StorageManager.get_local_copy(remote_url=pickle_data_url)
    with open(local_pkl, 'rb') as f:
        iris = pickle.load(f)
    df = pd.DataFrame(iris['data'], columns=iris['feature_names'])
    df['target'] = iris['target']
    return df

def step_two(data_frame, test_size=0.2, random_state=42):
    from sklearn.model_selection import train_test_split
    y = data_frame['target']
    X = data_frame.drop(columns=['target'])
    return train_test_split(X, y, test_size=test_size, random_state=random_state)

def step_three(data):
    from sklearn.linear_model import LogisticRegression
    X_train, X_test, y_train, y_test = data
    model = LogisticRegression(solver='lbfgs', max_iter=1000)
    model.fit(X_train, y_train)
    return model

if __name__ == '__main__':
    pipe = PipelineController(project='ClearML Tutorial', name='02_Pipeline_Experiment', version='1.0', add_pipeline_tags=True)
    pipe.add_parameter(name='url', default='https://github.com/allegroai/events/raw/master/odsc20-east/generic/iris_dataset.pkl')
    pipe.add_function_step(name='step_one', function=step_one, function_kwargs=dict(pickle_data_url='${pipeline.url}'), function_return=['data_frame'], cache_executed_step=True)
    pipe.add_function_step(name='step_two', function=step_two, function_kwargs=dict(data_frame='${step_one.data_frame}'), function_return=['processed_data'], cache_executed_step=True)
    pipe.add_function_step(name='step_three', function=step_three, function_kwargs=dict(data='${step_two.processed_data}'), function_return=['model'], cache_executed_step=True)
    pipe.start_locally(run_pipeline_steps_locally=True)
$ python3 02_pipeline.py

View the execution graph under the project in the web UI.

Run Hyperparameter Optimization

ClearML clones a completed base task and spawns trials across a defined search space:

from clearml import Task
from clearml.automation import HyperParameterOptimizer, DiscreteParameterRange, UniformIntegerParameterRange, RandomSearch

tasks = Task.get_tasks(project_name='ClearML Tutorial', task_filter={'status': ['completed', 'published']}, task_name='01_First_Experiment')
base_task_id = tasks[-1].id

Task.init(project_name='ClearML Tutorial', task_name='03_Hyperparameter_Optimization', task_type=Task.TaskTypes.optimizer)

optimizer = HyperParameterOptimizer(
    base_task_id=base_task_id,
    hyper_parameters=[
        UniformIntegerParameterRange('General/n_estimators', min_value=10, max_value=200, step_size=20),
        DiscreteParameterRange('General/max_depth', values=[3, 5, 7, 10])
    ],
    objective_metric_title='Performance',
    objective_metric_series='Accuracy',
    objective_metric_sign='max',
    optimizer_class=RandomSearch,
    max_number_of_concurrent_tasks=2,
    total_max_jobs=6
)
optimizer.start()
optimizer.wait()
top_exp = optimizer.get_top_experiments(1)
$ python3 03_hpo.py

Run the base experiment first — HPO needs a completed task to clone.

Serve Models with Triton

$ cd ~/clearml
$ git clone https://github.com/clearml/clearml-serving.git
$ pip install clearml-serving
$ clearml-serving create --name "serving-example"

Copy the printed Serving Service ID, then edit clearml-serving/docker/.env:

CLEARML_WEB_HOST="https://app.clearml.example.com"
CLEARML_API_HOST="https://api.clearml.example.com"
CLEARML_FILES_HOST="https://files.clearml.example.com"
CLEARML_API_ACCESS_KEY="YOUR_ACCESS_KEY"
CLEARML_API_SECRET_KEY="YOUR_SECRET_KEY"
CLEARML_SERVING_TASK_ID="SERVING_SERVICE_ID"
$ cd ~/clearml/clearml-serving/docker
$ docker compose --env-file .env -f docker-compose-triton.yml up -d
$ pip install -r ~/clearml/clearml-serving/examples/pytorch/requirements.txt
$ python3 ~/clearml/clearml-serving/examples/pytorch/train_pytorch_mnist.py

Grab the Model ID from the task's Artifacts tab, then register the endpoint:

$ clearml-serving --id SERVING_SERVICE_ID model add \
    --engine triton \
    --endpoint "test_model_pytorch" \
    --preprocess "clearml-serving/examples/pytorch/preprocess.py" \
    --model-id MODEL_ID \
    --input-size 1 28 28 \
    --input-name "INPUT__0" \
    --input-type float32 \
    --output-size 10 \
    --output-name "OUTPUT__0" \
    --output-type float32
$ docker compose --env-file .env -f docker-compose-triton.yml restart

Test it (replace SERVER-IP):

$ curl -X POST "http://SERVER-IP:8080/serve/test_model_pytorch" \
    -H "Content-Type: application/json" \
    -d '{"url": "https://raw.githubusercontent.com/clearml/clearml-serving/main/examples/pytorch/5.jpg"}'

Verify

$ curl -s https://api.clearml.example.com/debug.ping | head -c 100
$ curl -s -o /dev/null -w "%{http_code}" https://files.clearml.example.com/

Confirm the agent shows under Workers & Queues, the first experiment has metrics/artifacts, and cloning + enqueuing a modified experiment gets picked up by the agent.

Migrating from Azure ML

  • Experiments: azure.ai.ml job definitions → clearml.Task (auto-captures Git state, env, uncommitted changes).
  • Training jobs: managed compute clusters → agents on any hardware; task.execute_remotely() or enqueue via UI.
  • Pipelines: DAGs via azure.ai.mlPipelineController or @pipeline decorator.
  • HPO: HyperDrive → HyperParameterOptimizer, running on your own agents.
  • Model registry: azure.ai.ml registration → OutputModel, with full lineage.
  • Endpoints: Real-time/Batch Endpoints → ClearML Serving + Triton, any infrastructure.
  • Auth: Entra ID → API keys via clearml.conf or CLEARML_API_ACCESS_KEY/CLEARML_API_SECRET_KEY.
  • Compute targets → queues: name a compute target at submission vs. deploy agents and submit to a matching queue.
  • MLflow: existing mlflow.log_* calls route through ClearML's MLflow-compatible backend without a rewrite.

Next Steps

ClearML Server is running behind Traefik with an agent, tracked experiments, a pipeline, HPO, and a served model. From here:

  • Add more agents on GPU hosts and split queues by workload type
  • Wire pipeline runs into CI so training kicks off on every merge
  • Move the Elasticsearch/MongoDB volumes to backed-up, monitored storage before relying on this for production models

For the full guide, visit the original article on Vultr Docs.

DE
Source

This article was originally published by DEV Community and written by Sanskriti Harmukh.

Read original article on DEV Community
Back to Discover

Reading List