{"slug": "deploying-clearml-as-a-gcp-vertex-ai-alternative-on-ubuntu", "title": "Deploying ClearML as a GCP Vertex AI Alternative on Ubuntu", "summary": "A developer deployed ClearML, an open-source MLOps platform, as a self-hosted alternative to Google Cloud's Vertex AI on an Ubuntu server. The setup uses Docker Compose and Traefik to provide experiment tracking, pipelines, model registry, and Triton-based serving, covering the full ML lifecycle without vendor lock-in.", "body_md": "Google Vertex AI is Google Cloud's managed ML platform with experiment tracking, training jobs, pipelines, a model registry, and endpoints, but it locks you into GCP-specific APIs and per-use billing. ClearML is an open-source MLOps platform that covers the same ground on any Docker host or Kubernetes cluster, capturing training metrics automatically with no code changes and keeping every byte of data on infrastructure you control. This guide deploys ClearML Server with Docker Compose and Traefik, registers an agent, runs an experiment, builds a pipeline, runs a hyperparameter sweep, and deploys a Triton-served model. By the end, you'll have a self-hosted Vertex AI replacement covering the full ML lifecycle.\n\n| GCP Vertex AI | ClearML Equivalent | Purpose |\n|---|---|---|\n| Vertex AI Workbench | ClearML Web UI | Browser-based monitoring and configuration |\n| Vertex AI Experiments | ClearML Experiment Manager | Automatic hyperparameter/metric/artifact tracking |\n| Vertex AI Training Job | ClearML Agent + Tasks | Any machine becomes a remote worker via queues |\n| Vertex AI Pipelines | ClearML Pipelines | Python-native DAGs, no separate compile step |\n| Vertex AI Model Registry | ClearML Model Repository | Versioned models with full lineage |\n| Vertex AI Endpoints | ClearML Serving (Triton) | Self-hosted inference with canary/A-B support |\n| Cloud Monitoring/Logging | ClearML Scalars/Plots | Built-in metrics and hardware dashboards |\n\nPrerequisite:Ubuntu host with Docker + Compose, DNS A records for`app.clearml.example.com`\n\n,`api.clearml.example.com`\n\n,`files.clearml.example.com`\n\n. NVIDIA Container Toolkit if you'll run GPU agents.\n\n**1. Raise Elasticsearch's virtual memory limit and restart Docker:**\n\n``` bash\n$ echo \"vm.max_map_count=524288\" | sudo tee /etc/sysctl.d/99-clearml.conf\n$ sudo sysctl --system\n$ sudo systemctl restart docker\n```\n\n**2. Create persistent data directories with the correct ownership:**\n\n``` bash\n$ sudo mkdir -p /opt/clearml/{data/elastic_7,data/mongo_4/db,data/mongo_4/configdb,data/redis,data/fileserver,logs,config}\n$ sudo chown -R 1000:1000 /opt/clearml\n```\n\n**3. Download the official Compose file:**\n\n``` bash\n$ mkdir -p ~/clearml && cd ~/clearml\n$ curl -fsSL https://raw.githubusercontent.com/clearml/clearml-server/master/docker/docker-compose.yml -o docker-compose.yml\n```\n\n**4. Edit it:** comment out the `ports:`\n\nblocks under `apiserver`\n\n, `webserver`\n\n, and `fileserver`\n\n(Traefik will handle external routing), and replace the `networks:`\n\nsection with named bridges:\n\n```\nnetworks:\n  backend:\n    name: clearml_backend\n    driver: bridge\n  frontend:\n    name: clearml_frontend\n    driver: bridge\n```\n\n**5. Set the public hostnames:**\n\n``` bash\n$ nano .env\nCLEARML_WEB_HOST=https://app.clearml.example.com\nCLEARML_API_HOST=https://api.clearml.example.com\nCLEARML_FILES_HOST=https://files.clearml.example.com\n```\n\n**6. Start the stack:**\n\n``` bash\n$ docker compose up -d\n$ docker compose ps\n$ docker compose logs --tail 50\n```\n\n**1. Create the Traefik directory and cert store:**\n\n``` bash\n$ mkdir -p ~/clearml/traefik && cd ~/clearml/traefik\n$ mkdir -p letsencrypt && touch letsencrypt/acme.json && chmod 600 letsencrypt/acme.json\n```\n\n**2. Set the ACME email:**\n\n``` bash\n$ nano .env\nLETSENCRYPT_EMAIL=admin@example.com\n```\n\n**3. Create the Traefik Compose file:**\n\n``` bash\n$ nano docker-compose.yml\nservices:\n  traefik:\n    image: traefik:v3.6\n    container_name: traefik\n    command:\n      - \"--log.level=INFO\"\n      - \"--providers.file.filename=/etc/traefik/dynamic_conf.yml\"\n      - \"--entryPoints.web.address=:80\"\n      - \"--entryPoints.websecure.address=:443\"\n      - \"--entryPoints.web.http.redirections.entrypoint.to=websecure\"\n      - \"--certificatesResolvers.le.acme.httpChallenge.entryPoint=web\"\n      - \"--certificatesResolvers.le.acme.email=${LETSENCRYPT_EMAIL}\"\n      - \"--certificatesResolvers.le.acme.storage=/letsencrypt/acme.json\"\n    ports:\n      - \"80:80\"\n      - \"443:443\"\n    volumes:\n      - \"./letsencrypt:/letsencrypt\"\n      - \"./dynamic_conf.yml:/etc/traefik/dynamic_conf.yml:ro\"\n    networks:\n      - clearml-frontend\n    restart: unless-stopped\n\nnetworks:\n  clearml-frontend:\n    name: clearml_frontend\n    external: true\n```\n\n**4. Route each subdomain to its ClearML service:**\n\n``` bash\n$ nano dynamic_conf.yml\nhttp:\n  routers:\n    clearml-web:\n      rule: \"Host(`app.clearml.example.com`)\"\n      entryPoints: [websecure]\n      service: clearml-web\n      tls: {certResolver: le}\n    clearml-api:\n      rule: \"Host(`api.clearml.example.com`)\"\n      entryPoints: [websecure]\n      service: clearml-api\n      tls: {certResolver: le}\n    clearml-files:\n      rule: \"Host(`files.clearml.example.com`)\"\n      entryPoints: [websecure]\n      service: clearml-files\n      tls: {certResolver: le}\n  services:\n    clearml-web:\n      loadBalancer:\n        servers: [{url: \"http://clearml-webserver:80\"}]\n    clearml-api:\n      loadBalancer:\n        servers: [{url: \"http://clearml-apiserver:8008\"}]\n    clearml-files:\n      loadBalancer:\n        servers: [{url: \"http://clearml-fileserver:8081\"}]\n```\n\n**5. Start Traefik and confirm certificates issued:**\n\n``` bash\n$ docker compose up -d\n$ docker logs traefik 2>&1 | grep -i certificate\n```\n\n`https://app.clearml.example.com`\n\n, enter a username and company name, and click \n\n```\napi {\n  web_server: https://app.clearml.example.com\n  api_server: https://api.clearml.example.com\n  files_server: https://files.clearml.example.com\n  credentials {\n    \"access_key\" = \"YOUR_ACCESS_KEY\"\n    \"secret_key\" = \"YOUR_SECRET_KEY\"\n  }\n}\nbash\n$ mkdir -p ~/clearml-agent && cd ~/clearml-agent\n$ sudo apt install python3.12-venv -y\n$ python3 -m venv clearml_venv\n$ source clearml_venv/bin/activate\n$ pip install clearml-agent\n$ clearml-agent init\n```\n\nPaste the credentials block when prompted; press Enter to accept defaults for the rest. Then start the daemon:\n\n``` bash\n$ clearml-agent daemon --queue default --detached\n```\n\nFor GPU workloads:\n\n``` bash\n$ clearml-agent daemon --gpus 0,1 --queue default --detached\n```\n\nConfirm registration in **Workers & Queues → Workers**.\n\n``` bash\n$ source ~/clearml-agent/clearml_venv/bin/activate\n$ pip install clearml scikit-learn joblib pandas\n$ clearml-init\n```\n\nPaste the credentials block again when prompted.\n\n``` bash\n$ mkdir -p ~/clearml/experiments && cd ~/clearml/experiments\n$ nano 01_first_experiment.py\npython\nimport joblib\nfrom clearml import Task\nfrom sklearn.datasets import load_iris\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.ensemble import RandomForestClassifier\nfrom sklearn.metrics import accuracy_score, classification_report\n\ntask = Task.init(project_name='ClearML Tutorial', task_name='01_First_Experiment',\n                 tags=['tutorial', 'random-forest'])\n\nhyperparams = {'n_estimators': 100, 'max_depth': 5, 'random_state': 42}\ntask.connect(hyperparams)\n\niris = load_iris()\nX_train, X_test, y_train, y_test = train_test_split(\n    iris.data, iris.target, test_size=0.2, random_state=42)\n\nclf = RandomForestClassifier(**hyperparams).fit(X_train, y_train)\ny_pred = clf.predict(X_test)\naccuracy = accuracy_score(y_test, y_pred)\nreport = classification_report(y_test, y_pred, target_names=iris.target_names, output_dict=True)\n\nlogger = task.get_logger()\nlogger.report_scalar(title='Performance', series='Accuracy', value=accuracy, iteration=1)\nfor label, metrics in report.items():\n    if isinstance(metrics, dict):\n        for metric_name, value in metrics.items():\n            logger.report_scalar(title=f'Class: {label}', series=metric_name, value=value, iteration=1)\n\nmodel_path = 'iris_rf_model.pkl'\njoblib.dump(clf, model_path)\ntask.upload_artifact(name='trained_model', artifact_object=model_path)\ntask.close()\nbash\n$ python3 01_first_experiment.py\n```\n\nThe output prints a task URL — open it to see execution details, hyperparameters, artifacts, console output, and scalar charts under the **ClearML Tutorial** project.\n\n``` bash\n$ nano 02_pipeline.py\npython\nfrom clearml import PipelineController\n\ndef step_one(pickle_data_url):\n    import pickle, pandas as pd\n    from clearml import StorageManager\n    pickle_data_url = pickle_data_url or 'https://github.com/allegroai/events/raw/master/odsc20-east/generic/iris_dataset.pkl'\n    local_iris_pkl = StorageManager.get_local_copy(remote_url=pickle_data_url)\n    with open(local_iris_pkl, 'rb') as f: iris = pickle.load(f)\n    df = pd.DataFrame(iris['data'], columns=iris['feature_names']); df['target'] = iris['target']\n    return df\n\ndef step_two(data_frame, test_size=0.2, random_state=42):\n    from sklearn.model_selection import train_test_split\n    y = data_frame['target']; X = data_frame.drop(columns=['target'])\n    return train_test_split(X, y, test_size=test_size, random_state=random_state)\n\ndef step_three(data):\n    from sklearn.linear_model import LogisticRegression\n    X_train, X_test, y_train, y_test = data\n    return LogisticRegression(solver='lbfgs', max_iter=1000).fit(X_train, y_train)\n\nif __name__ == '__main__':\n    pipe = PipelineController(project='ClearML Tutorial', name='02_Pipeline_Experiment',\n                              version='1.0', add_pipeline_tags=True)\n    pipe.add_parameter('url', 'https://github.com/allegroai/events/raw/master/odsc20-east/generic/iris_dataset.pkl')\n    pipe.add_function_step('step_one',   step_one,   function_kwargs={'pickle_data_url': '${pipeline.url}'}, function_return=['data_frame'])\n    pipe.add_function_step('step_two',   step_two,   function_kwargs={'data_frame': '${step_one.data_frame}'}, function_return=['processed_data'])\n    pipe.add_function_step('step_three', step_three, function_kwargs={'data': '${step_two.processed_data}'}, function_return=['model'])\n    pipe.start_locally(run_pipeline_steps_locally=True)\nbash\n$ python3 02_pipeline.py\nbash\n$ nano 03_hpo.py\npython\nfrom clearml import Task\nfrom clearml.automation import HyperParameterOptimizer, DiscreteParameterRange, UniformIntegerParameterRange, RandomSearch\n\ntasks = Task.get_tasks(project_name='ClearML Tutorial',\n                       task_filter={'status': ['completed', 'published']},\n                       task_name='01_First_Experiment')\nbase_task_id = tasks[-1].id\n\nTask.init(project_name='ClearML Tutorial', task_name='03_Hyperparameter_Optimization',\n          task_type=Task.TaskTypes.optimizer)\n\noptimizer = HyperParameterOptimizer(\n    base_task_id=base_task_id,\n    hyper_parameters=[\n        UniformIntegerParameterRange('General/n_estimators', min_value=10, max_value=200, step_size=20),\n        DiscreteParameterRange('General/max_depth', values=[3, 5, 7, 10]),\n    ],\n    objective_metric_title='Performance', objective_metric_series='Accuracy',\n    objective_metric_sign='max', optimizer_class=RandomSearch,\n    max_number_of_concurrent_tasks=2, total_max_jobs=6,\n)\noptimizer.start(); optimizer.wait()\ntop = optimizer.get_top_experiments(1)\nif top:\n    print(top[0].id, top[0].get_parameters_as_dict().get('General', {}))\nbash\n$ python3 03_hpo.py\n```\n\nMonitor progress in the Web UI under **ClearML Tutorial**.\n\n``` bash\n$ cd ~/clearml\n$ git clone https://github.com/clearml/clearml-serving.git\n$ pip install clearml-serving\n$ clearml-serving create --name \"serving-example\"\n```\n\nSet the serving `.env`\n\n:\n\n``` bash\n$ nano clearml-serving/docker/.env\nCLEARML_WEB_HOST=\"https://app.clearml.example.com\"\nCLEARML_API_HOST=\"https://api.clearml.example.com\"\nCLEARML_FILES_HOST=\"https://files.clearml.example.com\"\nCLEARML_API_ACCESS_KEY=\"YOUR_ACCESS_KEY\"\nCLEARML_API_SECRET_KEY=\"YOUR_SECRET_KEY\"\nCLEARML_SERVING_TASK_ID=\"SERVING_SERVICE_ID\"\n```\n\nStart the Triton serving stack:\n\n``` bash\n$ cd ~/clearml/clearml-serving/docker\n$ docker compose --env-file .env -f docker-compose-triton.yml up -d\n```\n\nTrain and register a sample model:\n\n``` bash\n$ pip install -r ~/clearml/clearml-serving/examples/pytorch/requirements.txt\n$ python3 ~/clearml/clearml-serving/examples/pytorch/train_pytorch_mnist.py\n```\n\nCopy the Model ID from the task's **Artifacts** tab, then add the endpoint:\n\n``` bash\n$ clearml-serving --id SERVING_SERVICE_ID model add \\\n    --engine triton \\\n    --endpoint \"test_model_pytorch\" \\\n    --preprocess \"clearml-serving/examples/pytorch/preprocess.py\" \\\n    --model-id MODEL_ID \\\n    --input-size 1 28 28 --input-name \"INPUT__0\" --input-type float32 \\\n    --output-size 10 --output-name \"OUTPUT__0\" --output-type float32\n\n$ docker compose --env-file .env -f docker-compose-triton.yml restart\n```\n\nTest inference:\n\n``` bash\n$ curl -X POST \"http://SERVER_IP:8080/serve/test_model_pytorch\" \\\n    -H \"Content-Type: application/json\" \\\n    -d '{\"url\": \"https://raw.githubusercontent.com/clearml/clearml-serving/main/examples/pytorch/5.jpg\"}'\nbash\n$ curl -s https://api.clearml.example.com/debug.ping | head -c 100\n$ curl -s -o /dev/null -w \"%{http_code}\" https://files.clearml.example.com/\n```\n\nIn the Web UI: confirm the agent shows under **Workers & Queues**, the first experiment has metrics/artifacts, and cloning + re-enqueuing an experiment gets picked up by the agent.\n\n| Vertex AI concept | ClearML replacement |\n|---|---|\n`google.cloud.aiplatform` experiment tracking |\n`clearml.Task` — auto-captures Git state, deps, uncommitted changes |\n| Custom Training Jobs |\n`clearml.Task` + `task.execute_remotely()` , or enqueue via UI |\n| Vertex AI Workbench notebooks | Standard Jupyter/JupyterHub — `Task.init()` works identically |\n| Kubeflow-based Vertex Pipelines |\n`PipelineController` / `@pipeline` — Python-native, no compile step |\n| Vizier hyperparameter tuning |\n`HyperParameterOptimizer` — runs on your own agents |\n| Model Registry |\n`OutputModel` — full lineage to source experiment |\n| Vertex AI Endpoints | ClearML Serving + Triton — canary/A-B on your own infra |\n| Batch Prediction Jobs | No direct equivalent — migrate to standard Tasks writing to object storage |\n| GCS data access | ClearML works with GCS paths directly, or migrate to any storage |\n| IAM + ADC auth | API keys via `clearml.conf` or `CLEARML_API_ACCESS_KEY` /`SECRET_KEY`\n|\n`AIP_*` env conventions |\nNot needed — agents use standard Python/Docker, no Vertex-specific vars |\n| MLflow tracking calls | ClearML's MLflow compatibility layer routes `mlflow.log_*` without a rewrite |\n\nClearML is running with tracking, agents, pipelines, HPO, and Triton serving. From here you can:\n\nFor the full guide with additional tips, visit the original article on ** Vultr Docs**.", "url": "https://wpnews.pro/news/deploying-clearml-as-a-gcp-vertex-ai-alternative-on-ubuntu", "canonical_source": "https://dev.to/vultr/deploying-clearml-as-a-gcp-vertex-ai-alternative-on-ubuntu-n2f", "published_at": "2026-07-07 21:37:38+00:00", "updated_at": "2026-07-07 21:58:30.611559+00:00", "lang": "en", "topics": ["machine-learning", "mlops", "developer-tools", "ai-infrastructure"], "entities": ["ClearML", "Google Vertex AI", "Docker", "Traefik", "NVIDIA", "Elasticsearch", "MongoDB", "Redis"], "alternates": {"html": "https://wpnews.pro/news/deploying-clearml-as-a-gcp-vertex-ai-alternative-on-ubuntu", "markdown": "https://wpnews.pro/news/deploying-clearml-as-a-gcp-vertex-ai-alternative-on-ubuntu.md", "text": "https://wpnews.pro/news/deploying-clearml-as-a-gcp-vertex-ai-alternative-on-ubuntu.txt", "jsonld": "https://wpnews.pro/news/deploying-clearml-as-a-gcp-vertex-ai-alternative-on-ubuntu.jsonld"}}