cd /news/mlops/deploying-clearml-as-an-azure-ml-alt… · home topics mlops article
[ARTICLE · art-112142] src=dev.to ↗ pub= topic=mlops verified=true sentiment=· neutral

Deploying ClearML as an Azure ML Alternative

ClearML, an open-source MLOps platform, can be deployed as a self-hosted alternative to Azure Machine Learning, offering experiment tracking, pipelines, and model serving on any infrastructure. A developer's guide details setting up ClearML Server with Docker Compose and Traefik, configuring agents for remote execution, and integrating Triton for model serving, providing a cost-effective and portable solution for MLOps workflows.

read6 min views2 publishedAug 26, 2026

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 forapp.clearml.example.com

,api.clearml.example.com

,files.clearml.example.com

. GPU workloads (optional) need the NVIDIA Container Toolkit on the agent host.

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.

$ 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
bash
$ docker compose up -d
$ docker compose ps
$ docker compose logs --tail 50
bash
$ 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

https://app.clearml.example.com

, create the admin account (username + company name).

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"
  }
}

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.

$ 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

.

$ mkdir -p ~/clearml/experiments && cd ~/clearml/experiments
$ nano 01_first_experiment.py
python
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()
bash
$ 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.

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)
bash
$ python3 02_pipeline.py

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

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)
bash
$ python3 03_hpo.py

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

$ 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"
bash
$ 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"}'
bash
$ 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.

azure.ai.ml

job definitions → clearml.Task

(auto-captures Git state, env, uncommitted changes).task.execute_remotely()

or enqueue via UI.azure.ai.ml

PipelineController

or @pipeline

decorator.HyperParameterOptimizer

, running on your own agents.azure.ai.ml

registration → OutputModel

, with full lineage.clearml.conf

or CLEARML_API_ACCESS_KEY

/CLEARML_API_SECRET_KEY

.mlflow.log_*

calls route through ClearML's MLflow-compatible backend without a rewrite.ClearML Server is running behind Traefik with an agent, tracked experiments, a pipeline, HPO, and a served model. From here:

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

── more in #mlops 4 stories · sorted by recency
── more on @clearml 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/deploying-clearml-as…] indexed:0 read:6min 2026-08-26 ·