Repository: apache/camel-k
Pull request #1812 (2020-11-17 09:18:57)

chore(doc): Add Camel K operator monitoring documentation
This PR follows up on #1762.

**Release Note**
```release-note
NONE
```



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7d9c59108..0da18511a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,6 @@
 
 **Closed issues:**
 
-- scaling out camel-k integrations are not working correctly [\#1806](https://github.com/apache/camel-k/issues/1806)
 - Remove main runtime from operator codebase [\#1803](https://github.com/apache/camel-k/issues/1803)
 - Scalability of integration components in a non knative environment [\#1798](https://github.com/apache/camel-k/issues/1798)
 - Panic when attempting to convert from unstructured to Integration with traits [\#1794](https://github.com/apache/camel-k/issues/1794)
@@ -21,6 +20,7 @@
 - Add suport for gists [\#1740](https://github.com/apache/camel-k/issues/1740)
 - Update to k8s 0.18.8 [\#1698](https://github.com/apache/camel-k/issues/1698)
 - Error during unshare\(...\): Operation not permitted [\#1580](https://github.com/apache/camel-k/issues/1580)
+- Auto-detect json in yaml syntax [\#1549](https://github.com/apache/camel-k/issues/1549)
 - Provide yaml schema of design definition [\#1537](https://github.com/apache/camel-k/issues/1537)
 - Expose operator related metrics [\#1267](https://github.com/apache/camel-k/issues/1267)
 - helm.sh entries in go.sum [\#1231](https://github.com/apache/camel-k/issues/1231)
@@ -30,7 +30,10 @@
 
 **Merged pull requests:**
 
+- Update PDB trait documentation [\#1810](https://github.com/apache/camel-k/pull/1810) ([astefanutti](https://github.com/astefanutti))
+- Fix \#1549: auto-detect json in YAML [\#1809](https://github.com/apache/camel-k/pull/1809) ([nicolaferraro](https://github.com/nicolaferraro))
 - Fix \#1771: use registry creds for pull and push and set Spectrum as default [\#1807](https://github.com/apache/camel-k/pull/1807) ([nicolaferraro](https://github.com/nicolaferraro))
+- Add local run subcommand with basic support [\#1805](https://github.com/apache/camel-k/pull/1805) ([doru1004](https://github.com/doru1004))
 - chore\(ci\): simplify test configuration [\#1801](https://github.com/apache/camel-k/pull/1801) ([nicolaferraro](https://github.com/nicolaferraro))
 - Fix \#751: add guard to global operator [\#1800](https://github.com/apache/camel-k/pull/1800) ([nicolaferraro](https://github.com/nicolaferraro))
 - Fixes ENTESB-15219: only use default values when user doesn't provide a kamelet property value in integration [\#1796](https://github.com/apache/camel-k/pull/1796) ([dhirajsb](https://github.com/dhirajsb))
@@ -758,6 +761,7 @@
 - broken routes.js example [\#1065](https://github.com/apache/camel-k/issues/1065)
 - camel-k installation struggles [\#1059](https://github.com/apache/camel-k/issues/1059)
 - camel-k-maven-plugin must not contain any of these characters \/:"\<\>|?\* but found \> @ line 16, column 18 [\#1056](https://github.com/apache/camel-k/issues/1056)
+- Automatically extract trait configuration for user manual [\#1014](https://github.com/apache/camel-k/issues/1014)
 - Cannot set blockOwnerDeletion in this case because cannot find RESTMapping for APIVersion camel.apache.org/v1alpha1 Kind IntegrationKit [\#764](https://github.com/apache/camel-k/issues/764)
 - Error creating integration context with buildStrategy: routine on minikube [\#665](https://github.com/apache/camel-k/issues/665)
 - Kaniko: Add ability to push to on-premise registries [\#613](https://github.com/apache/camel-k/issues/613)
@@ -884,7 +888,6 @@
 **Closed issues:**
 
 - Failing start the camel-k-operator 1.0.0.-M3-SNAPSHOT with the minishifit [\#1015](https://github.com/apache/camel-k/issues/1015)
-- Automatically extract trait configuration for user manual [\#1014](https://github.com/apache/camel-k/issues/1014)
 - Provide a plain text file with the latest version of Camel K for easier automation of downloading via the tooling [\#1009](https://github.com/apache/camel-k/issues/1009)
 - Cannot use knative-source-yaml loader [\#1005](https://github.com/apache/camel-k/issues/1005)
 - Pubilsh Camel K 1.0.0-M2 to operator hub [\#1004](https://github.com/apache/camel-k/issues/1004)
@@ -1014,6 +1017,7 @@
 - External kamel bin testing [\#894](https://github.com/apache/camel-k/pull/894) ([bouskaJ](https://github.com/bouskaJ))
 - BuildConfig's, ImageStream's and Pod's not labeled with camel-k and can't delete with -l 'app=camel-k' [\#884](https://github.com/apache/camel-k/pull/884) ([dmvolod](https://github.com/dmvolod))
 - Fix embedded resources snapshot version [\#882](https://github.com/apache/camel-k/pull/882) ([dmvolod](https://github.com/dmvolod))
+- Switch to images-dev build on install-mini\<plaform\> [\#881](https://github.com/apache/camel-k/pull/881) ([dmvolod](https://github.com/dmvolod))
 - Added unit tests for ingress\_trait \#255 [\#880](https://github.com/apache/camel-k/pull/880) ([aldettinger](https://github.com/aldettinger))
 - feat: Enable integration scale sub-resource [\#962](https://github.com/apache/camel-k/pull/962) ([astefanutti](https://github.com/astefanutti))
 - fix: Add option to disable Kaniko cache warming [\#922](https://github.com/apache/camel-k/pull/922) ([asifdxtreme](https://github.com/asifdxtreme))
@@ -1072,7 +1076,6 @@
 
 **Merged pull requests:**
 
-- Switch to images-dev build on install-mini\<plaform\> [\#881](https://github.com/apache/camel-k/pull/881) ([dmvolod](https://github.com/dmvolod))
 - Fix make clean for tests and remove binary file from repo [\#879](https://github.com/apache/camel-k/pull/879) ([dmvolod](https://github.com/dmvolod))
 - chore\(js\): fix examples, use arrow function for lambda style processor [\#877](https://github.com/apache/camel-k/pull/877) ([lburgazzoli](https://github.com/lburgazzoli))
 - fix\(GC\): Skip 503 errors when scanning for resources to be GCed [\#876](https://github.com/apache/camel-k/pull/876) ([astefanutti](https://github.com/astefanutti))
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index df04c1dcd..38108d0b3 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -24,6 +24,10 @@
 ** xref:configuration/configmap-secret.adoc[ConfigMap/Secret]
 * Observability
 ** xref:observability/monitoring.adoc[Monitoring]
+*** xref:observability/operator.adoc[Operator Monitoring]
+*** xref:observability/integration.adoc[Integration Monitoring]
+* Scaling
+** xref:scaling/integration.adoc[Integration Scaling]
 * xref:traits:traits.adoc[Traits]
 // Start of autogenerated code - DO NOT EDIT! (trait-nav)
 ** xref:traits:3scale.adoc[3scale]
diff --git a/docs/modules/ROOT/pages/observability/integration.adoc b/docs/modules/ROOT/pages/observability/integration.adoc
new file mode 100644
index 000000000..e20b5b842
--- /dev/null
+++ b/docs/modules/ROOT/pages/observability/integration.adoc
@@ -0,0 +1,108 @@
+[[integration-monitoring]]
+= Camel K Integration Monitoring
+
+NOTE: The Camel K monitoring architecture relies on https://prometheus.io[Prometheus] and the eponymous operator. Make sure you've checked the xref:observability/monitoring.adoc#prerequisites[Camel K monitoring prerequisites].
+
+[[instrumentation]]
+== Instrumentation
+
+The xref:traits:prometheus.adoc[Prometheus trait] automates the configuration of integration pods to expose a _metrics_ endpoint, that can be discovered and scraped by a Prometheus server.
+
+The Prometheus trait can be enabled when running an integration, e.g.:
+
+[source,sh]
+----
+$ kamel run -t prometheus.enabled=true ...
+----
+
+Alternatively, the Prometheus trait can be enabled globally once, by updating the integration platform, e.g.:
+
+[source,sh]
+----
+$ kubectl patch ip camel-k --type=merge -p '{"spec":{"traits":{"prometheus":{"configuration":{"enabled":"true"}}}}}'
+----
+
+The underlying instrumentation mechanism depends on the configured integration runtime.
+As a result, the set of registered metrics, as well as the naming convention they follow, also depends on it.
+
+=== Main
+
+When the default, a.k.a. _main_, runtime is configured for the integration, the https://github.com/prometheus/jmx_exporter[JMX exporter] is responsible for collecting and exposing metrics from JMX mBeans.
+
+A custom configuration for the JMX exporter can be used by setting the `prometheus.configmap` parameter from the Prometheus trait with the name of a ConfigMap containing a `prometheus-jmx-exporter.yaml` key, e.g.:
+
+[source,sh]
+----
+$ kamel run -t prometheus.enabled=true -t prometheus.configmap=<jmx_exporter_config>...
+----
+
+Otherwise, the Prometheus trait uses a default configuration.
+
+=== Quarkus
+
+When the Quarkus runtime is configured for the integration, the xref:latest@camel-quarkus::reference/extensions/microprofile-metrics.adoc[Camel Quarkus MicroProfile Metrics extension] is responsible for collecting and exposing metrics in the https://github.com/OpenObservability/OpenMetrics[OpenMetrics] text format.
+
+The MicroProfile Metrics extension registers and exposes the following metrics out-of-the-box:
+
+* https://github.com/eclipse/microprofile-metrics/blob/master/spec/src/main/asciidoc/required-metrics.adoc#required-metrics[JVM and operating system related metrics]
+
+* xref:latest@camel-quarkus::reference/extensions/microprofile-metrics.adoc#_camel_route_metrics[Camel specific metrics]
+
+It is possible to extend this set of metrics by using either, or both:
+
+* The xref:latest@components::microprofile-metrics-component.adoc[MicroProfile Metrics component]
+
+* The https://github.com/eclipse/microprofile-metrics/blob/master/spec/src/main/asciidoc/app-programming-model.adoc#annotations[MicroProfile Metrics annotations], in external dependencies
+
+== Discovery
+
+The Prometheus trait automatically configures the resources necessary for the Prometheus Operator to reconcile, so that the managed Prometheus instance can scrape the integration _metrics_ endpoint.
+
+By default, the Prometheus trait creates a `ServiceMonitor` resource, with the `camel.apache.org/integration` label, which must match the `serviceMonitorSelector` field from the `Prometheus` resource.
+Additional labels can be specified with the `service-monitor-labels` parameter from the Prometheus trait, e.g.:
+
+[source,sh]
+----
+$ kamel run -t prometheus.service-monitor-labels="label_to_be_match_by=prometheus_selector" ...
+----
+
+The creation of the `ServiceMonitor` resource can be disabled using the `service-monitor` parameter, e.g.:
+
+[source,sh]
+----
+$ kamel run -t prometheus.service-monitor=false ...
+----
+
+More information can be found in the xref:traits:prometheus.adoc[Prometheus trait] documentation.
+
+The Prometheus Operator https://github.com/coreos/prometheus-operator/blob/v0.38.0/Documentation/user-guides/getting-started.md#related-resources[getting started] guide documents the discovery mechanism, as well as the relationship between the operator resources.
+
+In case your integration metrics are not discovered, you may want to rely on https://github.com/coreos/prometheus-operator/blob/v0.38.0/Documentation/troubleshooting.md#troubleshooting-servicemonitor-changes[Troubleshooting `ServiceMonitor` changes].
+
+== Alerting
+
+The Prometheus Operator declares the `AlertManager` resource that can be used to configure _Alertmanager_ instances, along with `Prometheus` instances.
+
+Assuming an `AlertManager` resource already exists in your cluster, you can register a `PrometheusRule` resource that is used by Prometheus to trigger alerts, e.g.:
+
+[source,sh]
+----
+$ cat <<EOF | kubectl apply -f -
+apiVersion: monitoring.coreos.com/v1
+kind: PrometheusRule
+metadata:
+  labels:
+    prometheus: example
+    role: alert-rules
+  name: prometheus-rules
+spec:
+  groups:
+  - name: camel-k.rules
+    rules:
+    - alert: CamelKAlert
+      expr: application_camel_context_exchanges_failed_total > 0
+EOF
+----
+
+More information can be found in the Prometheus Operator https://github.com/coreos/prometheus-operator/blob/v0.38.0/Documentation/user-guides/alerting.md[Alerting] user guide.
+You can also find more details in https://docs.openshift.com/container-platform/4.4/monitoring/monitoring-your-own-services.html#creating-alerting-rules_monitoring-your-own-services[Creating alerting rules] from the OpenShift documentation.
diff --git a/docs/modules/ROOT/pages/observability/monitoring.adoc b/docs/modules/ROOT/pages/observability/monitoring.adoc
index 15c6ce786..18e6f2e2e 100644
--- a/docs/modules/ROOT/pages/observability/monitoring.adoc
+++ b/docs/modules/ROOT/pages/observability/monitoring.adoc
@@ -61,7 +61,7 @@ This needs to be enabled by following these instructions:
 
   $ oc -n openshift-monitoring edit configmap cluster-monitoring-config
 
-. Set the `techPreviewUserWorkload` setting to `true` under `data/config.yaml`:
+. Set the `enableUserWorkload` setting to `true` under `data/config.yaml`:
 +
 [source,yaml]
 ----
@@ -72,145 +72,26 @@ metadata:
   namespace: openshift-monitoring
 data:
   config.yaml: |
-    techPreviewUserWorkload:
-      enabled: true
-----
-
-On OpenShift versions prior to 4.3, or if you do not want to change your cluster monitoring stack configuration, you can refer to the <<Kubernetes>> section in order to deploy a separate Prometheus Operator instance.
-
-[[instrumentation]]
-== Instrumentation
-
-The xref:traits:prometheus.adoc[Prometheus trait] automates the configuration of integration pods to expose a _metrics_ endpoint, that can be discovered and scraped by a Prometheus server.
-
-The Prometheus trait can be enabled when running an integration, e.g.:
-
-[source,sh]
-----
-$ kamel run -t prometheus.enabled=true ...
-----
-
-Alternatively, the Prometheus trait can be enabled globally once, by updating the integration platform, e.g.:
-
-[source,sh]
-----
-$ kubectl patch ip camel-k --type=merge -p '{"spec":{"traits":{"prometheus":{"configuration":{"enabled":"true"}}}}}'
-----
-
-The underlying instrumentation mechanism depends on the configured integration runtime.
-As a result, the set of registered metrics, as well as the naming convention they follow, also depends on it.
-
-=== Main
-
-When the default, a.k.a. _main_, runtime is configured for the integration, the https://github.com/prometheus/jmx_exporter[JMX exporter] is responsible for collecting and exposing metrics from JMX mBeans.
-
-A custom configuration for the JMX exporter can be used by setting the `prometheus.configmap` parameter from the Prometheus trait with the name of a ConfigMap containing a `prometheus-jmx-exporter.yaml` key, e.g.:
-
-[source,sh]
-----
-$ kamel run -t prometheus.enabled=true -t prometheus.configmap=<jmx_exporter_config>...
-----
-
-Otherwise, the Prometheus trait uses a default configuration.
-
-=== Quarkus
-
-When the Quarkus runtime is configured for the integration, the xref:latest@camel-quarkus::reference/extensions/microprofile-metrics.adoc[Camel Quarkus MicroProfile Metrics extension] is responsible for collecting and exposing metrics in the https://github.com/OpenObservability/OpenMetrics[OpenMetrics] text format.
-
-The MicroProfile Metrics extension registers and exposes the following metrics out-of-the-box:
-
-* https://github.com/eclipse/microprofile-metrics/blob/master/spec/src/main/asciidoc/required-metrics.adoc#required-metrics[JVM and operating system related metrics]
-
-* xref:latest@camel-quarkus::reference/extensions/microprofile-metrics.adoc#_camel_route_metrics[Camel specific metrics]
-
-It is possible to extend this set of metrics by using either, or both:
-
-* The xref:latest@components::microprofile-metrics-component.adoc[MicroProfile Metrics component]
-
-* The https://github.com/eclipse/microprofile-metrics/blob/master/spec/src/main/asciidoc/app-programming-model.adoc#annotations[MicroProfile Metrics annotations], in external dependencies
-
-== Discovery
-
-The Prometheus trait automatically configures the resources necessary for the Prometheus Operator to reconcile, so that the managed Prometheus instance can scrape the integration _metrics_ endpoint.
-
-By default, the Prometheus trait creates a `ServiceMonitor` resource, with the `camel.apache.org/integration` label, which must match the `serviceMonitorSelector` field from the `Prometheus` resource.
-Additional labels can be specified with the `service-monitor-labels` parameter from the Prometheus trait, e.g.:
-
-[source,sh]
+    enableUserWorkload: true
 ----
-$ kamel run -t prometheus.service-monitor-labels="label_to_be_match_by=prometheus_selector" ...
-----
-
-The creation of the `ServiceMonitor` resource can be disabled using the `service-monitor` parameter, e.g.:
-
-[source,sh]
-----
-$ kamel run -t prometheus.service-monitor=false ...
-----
-
-More information can be found in the xref:traits:prometheus.adoc[Prometheus trait] documentation.
-
-The Prometheus Operator https://github.com/coreos/prometheus-operator/blob/v0.38.0/Documentation/user-guides/getting-started.md#related-resources[getting started] guide documents the discovery mechanism, as well as the relationship between the operator resources.
-
-In case your integration metrics are not discovered, you may want to rely on https://github.com/coreos/prometheus-operator/blob/v0.38.0/Documentation/troubleshooting.md#troubleshooting-servicemonitor-changes[Troubleshooting `ServiceMonitor` changes].
-
-== Alerting
-
-The Prometheus Operator declares the `AlertManager` resource that can be used to configure _Alertmanager_ instances, along with `Prometheus` instances.
-
-Assuming an `AlertManager` resource already exists in your cluster, you can register a `PrometheusRule` resource that is used by Prometheus to trigger alerts, e.g.:
-
-[source,sh]
+Note that, in OpenShift versions from 4.3 to 4.5, the configuration is as following:
++
+[source,yaml]
 ----
-$ cat <<EOF | kubectl apply -f -
-apiVersion: monitoring.coreos.com/v1
-kind: PrometheusRule
+apiVersion: v1
+kind: ConfigMap
 metadata:
-  labels:
-    prometheus: example
-    role: alert-rules
-  name: prometheus-rules
-spec:
-  groups:
-  - name: camel-k.rules
-    rules:
-    - alert: CamelKAlert
-      expr: application_camel_context_exchanges_failed_total > 0
-EOF
+  name: cluster-monitoring-config
+  namespace: openshift-monitoring
+data:
+  config.yaml: |
+    techPreviewUserWorkload:
+      enabled: true
 ----
 
-More information can be found in the Prometheus Operator https://github.com/coreos/prometheus-operator/blob/v0.38.0/Documentation/user-guides/alerting.md[Alerting] user guide. You can also find more details in https://docs.openshift.com/container-platform/4.4/monitoring/monitoring-your-own-services.html#creating-alerting-rules_monitoring-your-own-services[Creating alerting rules] from the OpenShift documentation.
-
-== Autoscaling
-
-Integration metrics can be exported for horizontal pod autoscaling (HPA), using the https://github.com/DirectXMan12/k8s-prometheus-adapter[custom metrics Prometheus adapter].
-If you have an OpenShift cluster, you can follow https://docs.openshift.com/container-platform/4.4/monitoring/exposing-custom-application-metrics-for-autoscaling.html[Exposing custom application metrics for autoscaling] to set it up.
-
-Assuming you have the Prometheus adapter up and running, you can create a `HorizontalPodAutoscaler` resource, e.g.:
+On OpenShift versions prior to 4.3, or if you do not want to change your cluster monitoring stack configuration, you can refer to the <<Kubernetes>> section in order to deploy a separate Prometheus Operator instance.
 
-[source,sh]
-----
-$ cat <<EOF | kubectl apply -f -
-apiVersion: autoscaling/v2beta2
-kind: HorizontalPodAutoscaler
-metadata:
-  name: camel-k-autoscaler
-spec:
-  scaleTargetRef:
-    apiVersion: camel.apache.org/v1
-    kind: Integration
-    name: example
-  minReplicas: 1
-  maxReplicas: 10
-  metrics:
-  - type: Pods
-    pods:
-      metric:
-        name: application_camel_context_exchanges_inflight_count
-      target:
-        type: AverageValue
-        averageValue: 1k
-EOF
-----
+=== What's Next
 
-More information can be found in https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/[Horizontal Pod Autoscaler] from the Kubernetes documentation.
+- xref:observability/operator.adoc[Camel K operator monitoring]
+- xref:observability/integration.adoc[Camel K integration monitoring]
diff --git a/docs/modules/ROOT/pages/observability/operator.adoc b/docs/modules/ROOT/pages/observability/operator.adoc
new file mode 100644
index 000000000..a46e6f5ac
--- /dev/null
+++ b/docs/modules/ROOT/pages/observability/operator.adoc
@@ -0,0 +1,176 @@
+[[operator-monitoring]]
+= Camel K Operator Monitoring
+
+NOTE: The Camel K monitoring architecture relies on https://prometheus.io[Prometheus] and the eponymous operator. Make sure you've checked the xref:observability/monitoring.adoc#prerequisites[Camel K monitoring prerequisites].
+
+[[installation]]
+== Installation
+
+The `kamel install` command provides the `--monitoring` option flag, that can be used to automatically creates the default resources required to monitor the Camel K operator, e.g.:
+
+[source,sh]
+----
+$ kamel install --monitoring=true
+----
+
+This creates:
+
+* a `PodMonitor` resource targeting the operator _metrics_ endpoint, so that the Prometheus server can scrape the <<metrics>> exposed by the operator;
+* a `PrometheusRule` resource with default alerting rules based on the exposed metrics. The <<alerting>> provides more details about these default rules.
+
+The `kamel install` command also provides the `--monitoring-port` option, that can be used to change the port of the operator monitoring endpoint, e.g.:
+
+[source,sh]
+----
+$ kamel install --monitoring=true --monitoring-port=8888
+----
+
+You can refer to the <<discovery>> and <<alerting>> sections in case you don't want to rely on the default monitoring configuration.
+
+[[metrics]]
+== Metrics
+
+The Camel K operator monitoring endpoint exposes the following metrics:
+
+.Camel K operator metrics
+|===
+|Name |Type |Description |Buckets |Labels
+
+| `camel_k_reconciliation_duration_seconds`
+| `HistogramVec`
+| Reconciliation request duration
+| 0.25s, 0.5s, 1s, 5s
+| `namespace`, `group`, `version`, `kind`, `result`: `Reconciled`\|`Errored`\|`Requeued`, `tag`: `""`\|`PlatformError`\|`UserError`
+
+| `camel_k_build_duration_seconds`
+| `HistogramVec`
+| Build duration
+| 30s, 1m, 1.5m, 2m, 5m, 10m
+| `result`: `Succeeded`\|`Error`
+
+| `camel_k_build_recovery_attempts`
+| `Histogram`
+| Build recovery attempts
+| 0, 1, 2, 3, 4, 5
+| `result`: `Succeeded`\|`Error`
+
+| `camel_k_build_queue_duration_seconds`
+| `Histogram`
+| Build queue duration
+| 5s, 15s, 30s, 1m, 5m,
+| N/A
+
+| `camel_k_integration_first_readiness_seconds`
+| `Histogram`
+| Time to first integration readiness
+| 5s, 10s, 30s, 1m, 2m
+| N/A
+
+|===
+
+[[discovery]]
+== Discovery
+
+A `PodMonitor` resource must be created for the Prometheus Operator to reconcile, so that the managed Prometheus instance can scrape the Camel K operator _metrics_ endpoint.
+
+As an example, hereafter is the `PodMonitor` resource that is created when executing the `kamel install --monitoring=true` command:
+
+.operator-pod-monitor.yaml
+[source,yaml]
+----
+apiVersion: monitoring.coreos.com/v1
+kind: PodMonitor
+metadata:
+  name: camel-k-operator
+  labels: # <1>
+    ...
+spec:
+  selector:
+    matchLabels: # <2>
+      app: "camel-k"
+      camel.apache.org/component: operator
+  podMetricsEndpoints:
+    - port: metrics
+----
+<1> The labels must match the `podMonitorSelector` field from the `Prometheus` resource
+<2> This label selector matches the Camel K operator Deployment labels
+
+The Prometheus Operator https://github.com/coreos/prometheus-operator/blob/v0.38.0/Documentation/user-guides/getting-started.md#related-resources[getting started] guide documents the discovery mechanism, as well as the relationship between the operator resources.
+
+In case your operator metrics are not discovered, you may want to rely on https://github.com/coreos/prometheus-operator/blob/v0.38.0/Documentation/troubleshooting.md#troubleshooting-servicemonitor-changes[Troubleshooting `ServiceMonitor` changes], which also applies to `PodMonitor` resources troubleshooting.
+
+[[alerting]]
+== Alerting
+
+NOTE: The Prometheus Operator declares the `AlertManager` resource that can be used to configure _Alertmanager_ instances, along with `Prometheus` instances. The following section assumes an `AlertManager` resource already exists in your cluster.
+
+A `PrometheusRule` resource can be created for the Prometheus Operator to reconcile, so that the managed AlertManager instance can trigger alerts, based on the metrics exposed by the Camel K operator.
+
+As an example, hereafter is the alerting rules that are defined in `PrometheusRule` resource that is created when executing the `kamel install --monitoring=true` command:
+
+.Camel K operator alerts
+|===
+|Name |Severity |Description
+
+| `CamelKReconciliationDuration`
+| warning
+| More than 10% of the reconciliation requests have their duration above 0.5s over at least 1 min.
+
+| `CamelKReconciliationFailure`
+| warning
+| More that 1% of the reconciliation requests have failed over at least 10 min.
+
+| `CamelKSuccessBuildDuration2m`
+| warning
+| More that 10% of the successful builds have their duration above 2 min over at least 1 min.
+
+| `CamelKSuccessBuildDuration5m`
+| critical
+| More than 1% of the successful builds have their duration above 5 min over at least 1 min.
+
+| `CamelKBuildError`
+| critical
+| More that 1% of the builds for have errored over at least 10 min.
+
+| `CamelKBuildQueueDuration1m`
+| warning
+| More that 1% of the builds have been queued for more than 1 min over at least 1 min.
+
+| `CamelKBuildQueueDuration5m`
+| critical
+| More that 1% of the builds have been queued for more than 5 min over at least 1 min.
+
+|===
+
+You can register your own `PrometheusRule` resources, to be used by Prometheus AlertManager instances to trigger alerts, e.g.:
+
+[source,yaml]
+----
+apiVersion: monitoring.coreos.com/v1
+kind: PrometheusRule
+metadata:
+  name: camel-k-alerts
+spec:
+  groups:
+    - name: camel-k-alerts
+      rules:
+        - alert: CamelKIntegrationTimeToReadiness
+          expr: |
+            (
+            1 - sum(rate(camel_k_integration_first_readiness_seconds_bucket{le="60"}[5m])) by (job)
+            /
+            sum(rate(camel_k_integration_first_readiness_seconds_count[5m])) by (job)
+            )
+            * 100
+            > 10
+          for: 1m
+          labels:
+            severity: warning
+          annotations:
+            message: |
+              {{ printf "%0.0f" $value }}% of the integrations
+              for {{ $labels.job }} have their first time to readiness above 1m.
+----
+
+More information can be found in the Prometheus Operator https://github.com/coreos/prometheus-operator/blob/v0.38.0/Documentation/user-guides/alerting.md[Alerting] user guide.
+You can also find more details in https://docs.openshift.com/container-platform/4.4/monitoring/monitoring-your-own-services.html#creating-alerting-rules_monitoring-your-own-services[Creating alerting rules] from the OpenShift documentation.
diff --git a/docs/modules/ROOT/pages/scaling/integration.adoc b/docs/modules/ROOT/pages/scaling/integration.adoc
new file mode 100644
index 000000000..c69e4b85d
--- /dev/null
+++ b/docs/modules/ROOT/pages/scaling/integration.adoc
@@ -0,0 +1,67 @@
+[[integration-scaling]]
+= Camel K Integration Scaling
+
+== Manual Scaling
+
+An Integration can be scaled using the `kubectl scale` command, e.g.:
+
+[source,sh]
+----
+$ kubectl scale it <integration_name> --replicas <number_of_replicas>
+----
+
+This can also be achieved by editing the Integration resource directly, e.g.:
+
+[source,sh]
+----
+$ kubectl patch it <integration_name> -p '{"spec":{"replicas":<number_of_replicas>}}'
+----
+
+The Integration also reports its number of replicas in the `.Status.Replicas` field, e.g.:
+
+[source,sh]
+----
+$ kubectl get it <integration_name> -o jsonpath='{.spec.replicas}'
+----
+
+== Autoscaling with HPA
+
+An Integration can automatically scale based on its CPU utilization and custom metrics using https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/[horizontal pod autoscaling (HPA)].
+
+For example, executing the following command creates an _autoscaler_ for the Integration, with target CPU utilization set to 80%, and the number of replicas between 2 and 5:
+
+[source,sh]
+----
+$ kubectl autoscale it <integration_name> --min=2 --max=5 --cpu-percent=80
+----
+
+xref:observability/integration.adoc[Integration metrics] can also be exported for horizontal pod autoscaling (HPA), using the https://github.com/DirectXMan12/k8s-prometheus-adapter[custom metrics Prometheus adapter], so that the Integration can scale automatically based on its own metrics.
+
+If you have an OpenShift cluster, you can follow https://docs.openshift.com/container-platform/4.4/monitoring/exposing-custom-application-metrics-for-autoscaling.html[Exposing custom application metrics for autoscaling] to set it up.
+
+Assuming you have the Prometheus adapter up and running, you can create a `HorizontalPodAutoscaler` resource based on a particular Integration metric, e.g.:
+
+[source,yaml]
+----
+apiVersion: autoscaling/v2beta2
+kind: HorizontalPodAutoscaler
+metadata:
+  name: camel-k-autoscaler
+spec:
+  scaleTargetRef:
+    apiVersion: camel.apache.org/v1
+    kind: Integration
+    name: example
+  minReplicas: 1
+  maxReplicas: 10
+  metrics:
+  - type: Pods
+    pods:
+      metric:
+        name: application_camel_context_exchanges_inflight_count
+      target:
+        type: AverageValue
+        averageValue: 1k
+----
+
+More information can be found in https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/[Horizontal Pod Autoscaler] from the Kubernetes documentation.
diff --git a/pkg/cmd/inspect.go b/pkg/cmd/inspect.go
index 1c2cbf4f5..40c0dc11c 100644
--- a/pkg/cmd/inspect.go
+++ b/pkg/cmd/inspect.go
@@ -18,28 +18,11 @@ limitations under the License.
 package cmd
 
 import (
-	"errors"
 	"fmt"
-	"io/ioutil"
-	"os"
-	"path"
-	"strings"
 
-	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
-	"github.com/apache/camel-k/pkg/builder/runtime"
-	"github.com/apache/camel-k/pkg/trait"
-	"github.com/apache/camel-k/pkg/util"
-	"github.com/apache/camel-k/pkg/util/camel"
-	"github.com/apache/camel-k/pkg/util/defaults"
-	"github.com/apache/camel-k/pkg/util/maven"
-	"github.com/scylladb/go-set/strset"
 	"github.com/spf13/cobra"
 )
 
-var acceptedDependencyTypes = []string{"bom", "camel", "camel-k", "camel-quarkus", "mvn", "github"}
-
-const defaultDependenciesDirectoryName = "dependencies"
-
 func newCmdInspect(rootCmdOptions *RootCmdOptions) (*cobra.Command, *inspectCmdOptions) {
 	options := inspectCmdOptions{
 		RootCmdOptions: rootCmdOptions,
@@ -56,9 +39,15 @@ will be generated by calling Maven and then printed in the selected output forma
 			if err := options.validate(args); err != nil {
 				return err
 			}
+			if err := options.init(); err != nil {
+				return err
+			}
 			if err := options.run(args); err != nil {
 				fmt.Println(err.Error())
 			}
+			if err := options.deinit(); err != nil {
+				return err
+			}
 
 			return nil
 		},
@@ -68,9 +57,7 @@ will be generated by calling Maven and then printed in the selected output forma
 	}
 
 	cmd.Flags().Bool("all-dependencies", false, "Compute transitive dependencies and move them to directory pointed to by the --dependencies-directory flag.")
-	cmd.Flags().StringArrayP("dependency", "d", nil, `Additional top-level dependency with the format:
-<type>:<dependency-name>
-where <type> is one of {`+strings.Join(acceptedDependencyTypes, "|")+`}.`)
+	cmd.Flags().StringArrayP("dependency", "d", nil, additionalDependencyUsageMessage)
 	cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml")
 
 	return &cmd, &options
@@ -84,195 +71,22 @@ type inspectCmdOptions struct {
 }
 
 func (command *inspectCmdOptions) validate(args []string) error {
-	// If no source files have been provided there is nothing to inspect.
-	if len(args) == 0 {
-		return errors.New("no integration files have been provided, nothing to inspect")
-	}
-
-	// Ensure source files exist.
-	for _, arg := range args {
-		// fmt.Printf("Validating file: %v\n", arg)
-		fileExists, err := util.FileExists(arg)
-
-		// Report any error.
-		if err != nil {
-			return err
-		}
-
-		// Signal file not found.
-		if !fileExists {
-			return errors.New("input file " + arg + " file does not exist")
-		}
-	}
-
-	// Validate list of additional dependencies i.e. make sure that each dependency has
-	// a valid type.
-	if command.AdditionalDependencies != nil {
-		for _, additionalDependency := range command.AdditionalDependencies {
-			dependencyComponents := strings.Split(additionalDependency, ":")
-
-			TypeIsValid := false
-			for _, dependencyType := range acceptedDependencyTypes {
-				if dependencyType == dependencyComponents[0] {
-					TypeIsValid = true
-				}
-			}
-
-			if !TypeIsValid {
-				return errors.New("Unexpected type for user-provided dependency: " + additionalDependency + ", check command usage for valid format.")
-			}
-
-		}
-	}
-
-	return nil
-}
-
-func (command *inspectCmdOptions) run(args []string) error {
-	// Fetch existing catalog or create new one if one does not already exist.
-	catalog, err := createCamelCatalog()
-
-	// Get top-level dependencies, this is the default behavior when no other options are provided.
-	// Do not output these options when transitive options are enbled.
-	dependencies, err := getTopLevelDependencies(catalog, command.OutputFormat, args, !command.AllDependencies)
-	if err != nil {
-		return err
-	}
-
-	// Add additional user-provided dependencies.
-	if command.AdditionalDependencies != nil {
-		for _, additionalDependency := range command.AdditionalDependencies {
-			dependencies = append(dependencies, additionalDependency)
-		}
-	}
-
-	// Top level dependencies are printed out.
-	if command.AllDependencies {
-		// If --all-dependencies flag is set, move all transitive dependencies in the --dependencies-directory.
-		err = getTransitiveDependencies(catalog, dependencies, command)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
+	return validateIntegrationForDependencies(args, command.AdditionalDependencies)
 }
 
-func getTopLevelDependencies(catalog *camel.RuntimeCatalog, format string, args []string, printDependencies bool) ([]string, error) {
-	// List of top-level dependencies.
-	dependencies := strset.New()
-
-	// Invoke the dependency inspector code for each source file.
-	for _, source := range args {
-		data, _, err := loadContent(source, false, false)
-		if err != nil {
-			return []string{}, err
-		}
-
-		sourceSpec := v1.SourceSpec{
-			DataSpec: v1.DataSpec{
-				Name:        path.Base(source),
-				Content:     data,
-				Compression: false,
-			},
-		}
-
-		// Extract list of top-level dependencies.
-		dependencies.Merge(trait.AddSourceDependencies(sourceSpec, catalog))
-	}
-
-	if printDependencies {
-		err := outputDependencies(dependencies.List(), format)
-		if err != nil {
-			return []string{}, err
-		}
-	}
-
-	return dependencies.List(), nil
-}
-
-func generateCatalog() (*camel.RuntimeCatalog, error) {
-	// A Camel catalog is requiref for this operatio.
-	settings := ""
-	mvn := v1.MavenSpec{
-		LocalRepository: "",
-	}
-	runtime := v1.RuntimeSpec{
-		Version:  defaults.DefaultRuntimeVersion,
-		Provider: v1.RuntimeProviderQuarkus,
-	}
-	providerDependencies := []maven.Dependency{}
-	catalog, err := camel.GenerateCatalogCommon(settings, mvn, runtime, providerDependencies)
-	if err != nil {
-		return nil, err
-	}
-
-	return catalog, nil
+func (command *inspectCmdOptions) init() error {
+	return createMavenWorkingDirectory()
 }
 
-func getTransitiveDependencies(
-	catalog *camel.RuntimeCatalog,
-	dependencies []string,
-	command *inspectCmdOptions) error {
-
-	mvn := v1.MavenSpec{
-		LocalRepository: "",
-	}
-
-	// Create Maven project.
-	project := runtime.GenerateQuarkusProjectCommon(
-		catalog.CamelCatalogSpec.Runtime.Metadata["camel-quarkus.version"],
-		defaults.DefaultRuntimeVersion, catalog.CamelCatalogSpec.Runtime.Metadata["quarkus.version"])
-
-	// Inject dependencies into Maven project.
-	err := camel.ManageIntegrationDependencies(&project, dependencies, catalog)
-	if err != nil {
-		return err
-	}
-
-	// Create local Maven context.
-	temporaryDirectory, err := ioutil.TempDir(os.TempDir(), "maven-")
-	if err != nil {
-		return err
-	}
-
-	// Maven local context to be used for generating the transitive dependencies.
-	mc := maven.NewContext(temporaryDirectory, project)
-	mc.LocalRepository = mvn.LocalRepository
-	mc.Timeout = mvn.GetTimeout().Duration
-
-	// Make maven command less verbose.
-	mc.AdditionalArguments = append(mc.AdditionalArguments, "-q")
-
-	err = runtime.BuildQuarkusRunnerCommon(mc)
-	if err != nil {
-		return err
-	}
-
-	// Compute dependencies.
-	content, err := runtime.ComputeQuarkusDependenciesCommon(mc, catalog.Runtime.Version)
-	if err != nil {
-		return err
-	}
-
-	// Compose artifacts list.
-	artifacts := []v1.Artifact{}
-	artifacts, err = runtime.ProcessQuarkusTransitiveDependencies(mc, content)
+func (command *inspectCmdOptions) run(args []string) error {
+	// Fetch dependencies.
+	dependencies, err := getDependencies(args, command.AdditionalDependencies, command.AllDependencies)
 	if err != nil {
 		return err
 	}
 
-	// Dump dependencies in the dependencies directory and construct the list of dependencies.
-	transitiveDependencies := []string{}
-	for _, entry := range artifacts {
-		transitiveDependencies = append(transitiveDependencies, entry.Location)
-	}
-
-	// Remove directory used for computing the dependencies.
-	defer os.RemoveAll(temporaryDirectory)
-
-	// Output transitive dependencies only if requested via the output format flag.
-	err = outputDependencies(transitiveDependencies, command.OutputFormat)
+	// Print dependencies.
+	err = outputDependencies(dependencies, command.OutputFormat)
 	if err != nil {
 		return err
 	}
@@ -280,65 +94,6 @@ func getTransitiveDependencies(
 	return nil
 }
 
-func outputDependencies(dependencies []string, format string) error {
-	if format != "" {
-		err := printDependencies(format, dependencies)
-		if err != nil {
-			return err
-		}
-	} else {
-		// Print output in text form.
-		for _, dep := range dependencies {
-			fmt.Printf("%v\n", dep)
-		}
-	}
-
-	return nil
-}
-
-func printDependencies(format string, dependecies []string) error {
-	switch format {
-	case "yaml":
-		data, err := util.DependenciesToYAML(dependecies)
-		if err != nil {
-			return err
-		}
-		fmt.Print(string(data))
-	case "json":
-		data, err := util.DependenciesToJSON(dependecies)
-		if err != nil {
-			return err
-		}
-		fmt.Print(string(data))
-	default:
-		return errors.New("unknown output format: " + format)
-	}
-	return nil
-}
-
-func getWorkingDirectory() (string, error) {
-	currentDirectory, err := os.Getwd()
-	if err != nil {
-		return "", err
-	}
-
-	return currentDirectory, nil
-}
-
-func createCamelCatalog() (*camel.RuntimeCatalog, error) {
-	// Attempt to reuse existing Camel catalog if one is present.
-	catalog, err := camel.DefaultCatalog()
-	if err != nil {
-		return nil, err
-	}
-
-	// Generate catalog if one was not found.
-	if catalog == nil {
-		catalog, err = generateCatalog()
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	return catalog, nil
+func (command *inspectCmdOptions) deinit() error {
+	return deleteMavenWorkingDirectory()
 }
diff --git a/pkg/cmd/local.go b/pkg/cmd/local.go
new file mode 100644
index 000000000..89b774044
--- /dev/null
+++ b/pkg/cmd/local.go
@@ -0,0 +1,55 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cmd
+
+import (
+	"github.com/pkg/errors"
+	"github.com/spf13/cobra"
+)
+
+// NewCmdLocal -- Add local kamel subcommand with several other subcommands of its own.
+func newCmdLocal(rootCmdOptions *RootCmdOptions) *cobra.Command {
+	cmd := cobra.Command{
+		Use:   "local [sub-command]",
+		Short: "Perform integration actions locally.",
+		Long:  `Perform integration actions locally given a set of input integration files.`,
+		Annotations: map[string]string{
+			offlineCommandLabel: "true",
+		},
+	}
+
+	return &cmd
+}
+
+func addLocalSubCommands(cmd *cobra.Command, options *RootCmdOptions) error {
+	var localCmd *cobra.Command
+	for _, c := range cmd.Commands() {
+		if c.Name() == "local" {
+			localCmd = c
+			break
+		}
+	}
+
+	if localCmd == nil {
+		return errors.New("could not find any configured local command")
+	}
+
+	localCmd.AddCommand(cmdOnly(newCmdLocalRun(options)))
+
+	return nil
+}
diff --git a/pkg/cmd/local_run.go b/pkg/cmd/local_run.go
new file mode 100644
index 000000000..f2b86f4c7
--- /dev/null
+++ b/pkg/cmd/local_run.go
@@ -0,0 +1,110 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cmd
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/spf13/cobra"
+)
+
+func newCmdLocalRun(rootCmdOptions *RootCmdOptions) (*cobra.Command, *localRunCmdOptions) {
+	options := localRunCmdOptions{
+		RootCmdOptions: rootCmdOptions,
+	}
+
+	cmd := cobra.Command{
+		Use:     "run [integration files]",
+		Short:   "Run integration locally.",
+		Long:    `Run integration locally using the input integration files.`,
+		PreRunE: decode(&options),
+		RunE: func(_ *cobra.Command, args []string) error {
+			if err := options.validate(args); err != nil {
+				return err
+			}
+			if err := options.init(); err != nil {
+				return err
+			}
+			if err := options.run(args); err != nil {
+				fmt.Println(err.Error())
+			}
+			if err := options.deinit(); err != nil {
+				return err
+			}
+
+			return nil
+		},
+		Annotations: map[string]string{
+			offlineCommandLabel: "true",
+		},
+	}
+
+	cmd.Flags().StringArray("property-file", nil, "Add a property file to the integration.")
+	cmd.Flags().StringArrayP("dependency", "d", nil, `Additional top-level dependency with the format:
+<type>:<dependency-name>
+where <type> is one of {`+strings.Join(acceptedDependencyTypes, "|")+`}.`)
+
+	return &cmd, &options
+}
+
+type localRunCmdOptions struct {
+	*RootCmdOptions
+	PropertyFiles          []string `mapstructure:"property-files"`
+	AdditionalDependencies []string `mapstructure:"dependencies"`
+}
+
+func (command *localRunCmdOptions) validate(args []string) error {
+	// Validate additional dependencies specified by the user.
+	err := validateIntegrationForDependencies(args, command.AdditionalDependencies)
+	if err != nil {
+		return err
+	}
+
+	// Validate properties file.
+	err = validateFiles(command.PropertyFiles)
+	if err != nil {
+		return nil
+	}
+
+	return nil
+}
+
+func (command *localRunCmdOptions) init() error {
+	return createMavenWorkingDirectory()
+}
+
+func (command *localRunCmdOptions) run(args []string) error {
+	// Fetch dependencies.
+	dependencies, err := getDependencies(args, command.AdditionalDependencies, true)
+	if err != nil {
+		return err
+	}
+
+	// Run the integration locally.
+	err = RunLocalIntegration(command.PropertyFiles, dependencies, args)
+	if err != nil {
+		return nil
+	}
+
+	return nil
+}
+
+func (command *localRunCmdOptions) deinit() error {
+	return deleteMavenWorkingDirectory()
+}
diff --git a/pkg/cmd/modeline.go b/pkg/cmd/modeline.go
index 4eab57a9e..6a3f0c139 100644
--- a/pkg/cmd/modeline.go
+++ b/pkg/cmd/modeline.go
@@ -31,6 +31,7 @@ import (
 
 const (
 	runCmdName        = "run"
+	localCmdName      = "local"
 	runCmdSourcesArgs = "source"
 )
 
@@ -98,9 +99,14 @@ func createKamelWithModelineCommand(ctx context.Context, args []string) (*cobra.
 
 	fg := target.Flags()
 
-	additionalSources, err := fg.GetStringArray(runCmdSourcesArgs)
-	if err != nil {
-		return nil, nil, err
+	// Only the run command has source flag (for now). Remove condition when
+	// local run also supports source.
+	additionalSources := make([]string, 0)
+	if target.Name() == runCmdName && target.Parent().Name() != localCmdName {
+		additionalSources, err = fg.GetStringArray(runCmdSourcesArgs)
+		if err != nil {
+			return nil, nil, err
+		}
 	}
 
 	files := make([]string, 0, len(fg.Args())+len(additionalSources))
@@ -155,27 +161,37 @@ func extractModelineOptions(ctx context.Context, sources []string) ([]modeline.O
 	}
 
 	for _, resolvedSource := range resolvedSources {
-		ops, err := modeline.Parse(resolvedSource.Location, resolvedSource.Content)
+		ops, err := extractModelineOptionsFromSource(resolvedSource)
 		if err != nil {
-			return opts, errors.Wrapf(err, "cannot process file %s", resolvedSource.Location)
+			return opts, err
 		}
-		for i, o := range ops {
-			if disallowedOptions[o.Name] {
-				return opts, fmt.Errorf("option %q is disallowed in modeline", o.Name)
-			}
 
-			if fileOptions[o.Name] && resolvedSource.Local {
-				baseDir := filepath.Dir(resolvedSource.Origin)
-				refPath := o.Value
-				if !filepath.IsAbs(refPath) {
-					full := path.Join(baseDir, refPath)
-					o.Value = full
-					ops[i] = o
-				}
-			}
-		}
 		opts = append(opts, ops...)
 	}
 
 	return opts, nil
 }
+
+func extractModelineOptionsFromSource(resolvedSource Source) ([]modeline.Option, error) {
+	ops, err := modeline.Parse(resolvedSource.Location, resolvedSource.Content)
+	if err != nil {
+		return ops, errors.Wrapf(err, "cannot process file %s", resolvedSource.Location)
+	}
+	for i, o := range ops {
+		if disallowedOptions[o.Name] {
+			return ops, fmt.Errorf("option %q is disallowed in modeline", o.Name)
+		}
+
+		if fileOptions[o.Name] && resolvedSource.Local {
+			baseDir := filepath.Dir(resolvedSource.Origin)
+			refPath := o.Value
+			if !filepath.IsAbs(refPath) {
+				full := path.Join(baseDir, refPath)
+				o.Value = full
+				ops[i] = o
+			}
+		}
+	}
+
+	return ops, nil
+}
diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go
index 2899a3087..c56d3690e 100644
--- a/pkg/cmd/root.go
+++ b/pkg/cmd/root.go
@@ -61,6 +61,10 @@ func NewKamelCommand(ctx context.Context) (*cobra.Command, error) {
 		return cmd, err
 	}
 
+	if err := addLocalSubCommands(cmd, &options); err != nil {
+		return cmd, err
+	}
+
 	err = kamelPostAddCommandInit(cmd)
 
 	return cmd, err
@@ -139,6 +143,7 @@ func addKamelSubcommands(cmd *cobra.Command, options *RootCmdOptions) {
 	cmd.AddCommand(cmdOnly(newCmdInit(options)))
 	cmd.AddCommand(cmdOnly(newCmdDebug(options)))
 	cmd.AddCommand(cmdOnly(newCmdInspect(options)))
+	cmd.AddCommand(newCmdLocal(options))
 }
 
 func addHelpSubCommands(cmd *cobra.Command, options *RootCmdOptions) error {
diff --git a/pkg/cmd/util.go b/pkg/cmd/util.go
index 8d5ac1470..3e364a621 100644
--- a/pkg/cmd/util.go
+++ b/pkg/cmd/util.go
@@ -24,11 +24,12 @@ import (
 	"encoding/csv"
 	"encoding/json"
 	"fmt"
-	"github.com/apache/camel-k/pkg/util/gzip"
 	"log"
 	"reflect"
 	"strings"
 
+	"github.com/apache/camel-k/pkg/util/gzip"
+
 	"github.com/mitchellh/mapstructure"
 
 	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
diff --git a/pkg/cmd/util_commands.go b/pkg/cmd/util_commands.go
new file mode 100644
index 000000000..b44243a21
--- /dev/null
+++ b/pkg/cmd/util_commands.go
@@ -0,0 +1,95 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cmd
+
+import (
+	"context"
+	"fmt"
+	"os"
+	"os/exec"
+	"path"
+	"strings"
+)
+
+var (
+	ctx9, cancel9 = context.WithCancel(context.Background()) // preemptive: kill subprocess
+	ctx, cancel   = context.WithCancel(ctx9)                 // cooperative: wait for subprocess
+)
+
+func formatRoutes(files []string) []string {
+	routes := []string{}
+	for _, route := range files {
+		// Split route path.
+		a := strings.Split(route, ".")
+
+		// Extract extension.
+		extension := a[len(a)-1]
+
+		// Add file if extension is supported.
+		routes = append(routes, "file:"+route+"?language="+extension)
+	}
+
+	return routes
+}
+
+func confDirectories(properties []string) []string {
+	confDirs := []string{}
+
+	for _, propertiesPath := range properties {
+		confDirs = append(confDirs, path.Dir(propertiesPath))
+	}
+
+	return confDirs
+}
+
+func assembleClasspatchArgValue(properties []string, dependencies []string, routes []string) string {
+	classpathContents := []string{}
+	classpathContents = append(classpathContents, properties...)
+	classpathContents = append(classpathContents, routes...)
+	classpathContents = append(classpathContents, dependencies...)
+	return strings.Join(classpathContents, ":")
+}
+
+// RunLocalIntegration --
+func RunLocalIntegration(properties []string, dependencies []string, routes []string) error {
+	// Create classpath value.
+	classpathValue := assembleClasspatchArgValue(properties, dependencies, routes)
+
+	// Create java command that runs the integration.
+	javaCmd := "java"
+
+	// Create java command arguments.
+	args := make([]string, 0)
+	args = append(args, "-cp")
+	args = append(args, classpathValue)
+	args = append(args, "io.quarkus.runner.GeneratedMain")
+
+	cmd := exec.CommandContext(ctx, javaCmd, args...)
+	cmd.Stderr = os.Stderr
+	cmd.Stdout = os.Stdout
+
+	fmt.Printf("executing: %s", strings.Join(cmd.Args, " "))
+
+	// Add directory where the properties file resides.
+	cmd.Env = append(cmd.Env, "CAMEL_K_CONF_D="+strings.Join(confDirectories(properties), ","))
+
+	// Add files to the command line under the CAMEL_K_ROUTES flag.
+	cmd.Env = append(cmd.Env, "CAMEL_K_ROUTES="+strings.Join(formatRoutes(routes), ","))
+
+	return cmd.Run()
+}
diff --git a/pkg/cmd/util_dependencies.go b/pkg/cmd/util_dependencies.go
new file mode 100644
index 000000000..30722a76c
--- /dev/null
+++ b/pkg/cmd/util_dependencies.go
@@ -0,0 +1,326 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cmd
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path"
+	"strings"
+
+	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+	"github.com/apache/camel-k/pkg/builder/runtime"
+	"github.com/apache/camel-k/pkg/trait"
+	"github.com/apache/camel-k/pkg/util"
+	"github.com/apache/camel-k/pkg/util/camel"
+	"github.com/apache/camel-k/pkg/util/defaults"
+	"github.com/apache/camel-k/pkg/util/maven"
+	"github.com/pkg/errors"
+	"github.com/scylladb/go-set/strset"
+)
+
+// Directory used by Maven for an invocation of the kamel local command.
+// By default a temporary folder will be used.
+var mavenWorkingDirectory string = ""
+
+var acceptedDependencyTypes = []string{"bom", "camel", "camel-k", "camel-quarkus", "mvn", "github"}
+
+var additionalDependencyUsageMessage = `Additional top-level dependencies are specified with the format:
+<type>:<dependency-name>
+where <type> is one of {` + strings.Join(acceptedDependencyTypes, "|") + `}.`
+
+const defaultDependenciesDirectoryName = "dependencies"
+
+func getDependencies(args []string, additionalDependencies []string, allDependencies bool) ([]string, error) {
+	// Fetch existing catalog or create new one if one does not already exist.
+	catalog, err := createCamelCatalog()
+
+	// Get top-level dependencies.
+	dependencies, err := getTopLevelDependencies(catalog, args)
+	if err != nil {
+		return nil, err
+	}
+
+	// Add additional user-provided dependencies.
+	if additionalDependencies != nil {
+		for _, additionalDependency := range additionalDependencies {
+			dependencies = append(dependencies, additionalDependency)
+		}
+	}
+
+	// Compute transitive dependencies.
+	if allDependencies {
+		dependencies, err = getTransitiveDependencies(catalog, dependencies)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return dependencies, nil
+}
+
+func getTopLevelDependencies(catalog *camel.RuntimeCatalog, args []string) ([]string, error) {
+	// List of top-level dependencies.
+	dependencies := strset.New()
+
+	// Invoke the dependency inspector code for each source file.
+	for _, source := range args {
+		data, _, err := loadContent(source, false, false)
+		if err != nil {
+			return []string{}, err
+		}
+
+		sourceSpec := v1.SourceSpec{
+			DataSpec: v1.DataSpec{
+				Name:        path.Base(source),
+				Content:     data,
+				Compression: false,
+			},
+		}
+
+		// Extract list of top-level dependencies.
+		dependencies.Merge(trait.AddSourceDependencies(sourceSpec, catalog))
+	}
+
+	return dependencies.List(), nil
+}
+
+func getTransitiveDependencies(
+	catalog *camel.RuntimeCatalog,
+	dependencies []string) ([]string, error) {
+
+	mvn := v1.MavenSpec{
+		LocalRepository: "",
+	}
+
+	// Create Maven project.
+	project := runtime.GenerateQuarkusProjectCommon(
+		catalog.CamelCatalogSpec.Runtime.Metadata["camel-quarkus.version"],
+		defaults.DefaultRuntimeVersion, catalog.CamelCatalogSpec.Runtime.Metadata["quarkus.version"])
+
+	// Inject dependencies into Maven project.
+	err := camel.ManageIntegrationDependencies(&project, dependencies, catalog)
+	if err != nil {
+		return nil, err
+	}
+
+	// Maven local context to be used for generating the transitive dependencies.
+	mc := maven.NewContext(mavenWorkingDirectory, project)
+	mc.LocalRepository = mvn.LocalRepository
+	mc.Timeout = mvn.GetTimeout().Duration
+
+	// Make maven command less verbose.
+	mc.AdditionalArguments = append(mc.AdditionalArguments, "-q")
+
+	err = runtime.BuildQuarkusRunnerCommon(mc)
+	if err != nil {
+		return nil, err
+	}
+
+	// Compute dependencies.
+	content, err := runtime.ComputeQuarkusDependenciesCommon(mc, catalog.Runtime.Version)
+	if err != nil {
+		return nil, err
+	}
+
+	// Compose artifacts list.
+	artifacts := []v1.Artifact{}
+	artifacts, err = runtime.ProcessQuarkusTransitiveDependencies(mc, content)
+	if err != nil {
+		return nil, err
+	}
+
+	// Dump dependencies in the dependencies directory and construct the list of dependencies.
+	transitiveDependencies := []string{}
+	for _, entry := range artifacts {
+		transitiveDependencies = append(transitiveDependencies, entry.Location)
+	}
+
+	return transitiveDependencies, nil
+}
+
+func generateCatalog() (*camel.RuntimeCatalog, error) {
+	// A Camel catalog is requiref for this operatio.
+	settings := ""
+	mvn := v1.MavenSpec{
+		LocalRepository: "",
+	}
+	runtime := v1.RuntimeSpec{
+		Version:  defaults.DefaultRuntimeVersion,
+		Provider: v1.RuntimeProviderQuarkus,
+	}
+	providerDependencies := []maven.Dependency{}
+	catalog, err := camel.GenerateCatalogCommon(settings, mvn, runtime, providerDependencies)
+	if err != nil {
+		return nil, err
+	}
+
+	return catalog, nil
+}
+
+func createCamelCatalog() (*camel.RuntimeCatalog, error) {
+	// Attempt to reuse existing Camel catalog if one is present.
+	catalog, err := camel.DefaultCatalog()
+	if err != nil {
+		return nil, err
+	}
+
+	// Generate catalog if one was not found.
+	if catalog == nil {
+		catalog, err = generateCatalog()
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return catalog, nil
+}
+
+func outputDependencies(dependencies []string, format string) error {
+	if format != "" {
+		err := printDependencies(format, dependencies)
+		if err != nil {
+			return err
+		}
+	} else {
+		// Print output in text form.
+		for _, dep := range dependencies {
+			fmt.Printf("%v\n", dep)
+		}
+	}
+
+	return nil
+}
+
+func printDependencies(format string, dependecies []string) error {
+	switch format {
+	case "yaml":
+		data, err := util.DependenciesToYAML(dependecies)
+		if err != nil {
+			return err
+		}
+		fmt.Print(string(data))
+	case "json":
+		data, err := util.DependenciesToJSON(dependecies)
+		if err != nil {
+			return err
+		}
+		fmt.Print(string(data))
+	default:
+		return errors.New("unknown output format: " + format)
+	}
+	return nil
+}
+
+func validateFile(file string) error {
+	fileExists, err := util.FileExists(file)
+
+	// Report any error.
+	if err != nil {
+		return err
+	}
+
+	// Signal file not found.
+	if !fileExists {
+		return errors.New("File " + file + " file does not exist")
+	}
+
+	return nil
+}
+
+func validateFiles(args []string) error {
+	// Ensure source files exist.
+	for _, arg := range args {
+		err := validateFile(arg)
+		if err != nil {
+			return nil
+		}
+	}
+
+	return nil
+}
+
+func validateAdditionalDependencies(additionalDependencies []string) error {
+	// Validate list of additional dependencies i.e. make sure that each dependency has
+	// a valid type.
+	if additionalDependencies != nil {
+		for _, additionalDependency := range additionalDependencies {
+			isValid := validateDependency(additionalDependency)
+			if !isValid {
+				return errors.New("Unexpected type for user-provided dependency: " + additionalDependency + ". " + additionalDependencyUsageMessage)
+			}
+		}
+	}
+
+	return nil
+}
+
+func validateDependency(additionalDependency string) bool {
+	dependencyComponents := strings.Split(additionalDependency, ":")
+
+	TypeIsValid := false
+	for _, dependencyType := range acceptedDependencyTypes {
+		if dependencyType == dependencyComponents[0] {
+			TypeIsValid = true
+		}
+	}
+
+	return TypeIsValid
+}
+
+func validateIntegrationForDependencies(args []string, additionalDependencies []string) error {
+	// If no source files have been provided there is nothing to inspect.
+	if len(args) == 0 {
+		return errors.New("no integration files have been provided")
+	}
+
+	// Validate integration files.
+	err := validateFiles(args)
+	if err != nil {
+		return nil
+	}
+
+	// Validate additional dependencies specified by the user.
+	err = validateAdditionalDependencies(additionalDependencies)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func createMavenWorkingDirectory() error {
+	// Create local Maven context.
+	temporaryDirectory, err := ioutil.TempDir(os.TempDir(), "maven-")
+	if err != nil {
+		return err
+	}
+
+	// Set the Maven directory to the default value.
+	mavenWorkingDirectory = temporaryDirectory
+
+	return nil
+}
+
+func deleteMavenWorkingDirectory() error {
+	// Remove directory used for computing the dependencies.
+	defer os.RemoveAll(mavenWorkingDirectory)
+
+	return nil
+}
diff --git a/pkg/cmd/util_sources.go b/pkg/cmd/util_sources.go
index af218f044..50a083859 100644
--- a/pkg/cmd/util_sources.go
+++ b/pkg/cmd/util_sources.go
@@ -66,27 +66,10 @@ func ResolveSources(ctx context.Context, locations []string, compress bool) ([]S
 
 	for _, location := range locations {
 		if isLocal(location) {
-			if _, err := os.Stat(location); err != nil && os.IsNotExist(err) {
-				return sources, errors.Wrapf(err, "file %s does not exist", location)
-			} else if err != nil {
-				return sources, errors.Wrapf(err, "error while accessing file %s", location)
-			}
-
-			answer := Source{
-				Name:     path.Base(location),
-				Origin:   location,
-				Location: location,
-				Compress: compress,
-				Local:    true,
-			}
-
-			content, err := ioutil.ReadFile(location)
+			answer, err := ResolveLocalSource(location, compress)
 			if err != nil {
 				return sources, err
 			}
-			if err := answer.setContent(content); err != nil {
-				return sources, err
-			}
 
 			sources = append(sources, answer)
 		} else {
@@ -199,3 +182,30 @@ func ResolveSources(ctx context.Context, locations []string, compress bool) ([]S
 
 	return sources, nil
 }
+
+// ResolveLocalSource --
+func ResolveLocalSource(location string, compress bool) (Source, error) {
+	if _, err := os.Stat(location); err != nil && os.IsNotExist(err) {
+		return Source{}, errors.Wrapf(err, "file %s does not exist", location)
+	} else if err != nil {
+		return Source{}, errors.Wrapf(err, "error while accessing file %s", location)
+	}
+
+	answer := Source{
+		Name:     path.Base(location),
+		Origin:   location,
+		Location: location,
+		Compress: compress,
+		Local:    true,
+	}
+
+	content, err := ioutil.ReadFile(location)
+	if err != nil {
+		return Source{}, err
+	}
+	if err := answer.setContent(content); err != nil {
+		return Source{}, err
+	}
+
+	return answer, nil
+}
diff --git a/pkg/util/camel/camel_runtime_catalog.go b/pkg/util/camel/camel_runtime_catalog.go
index 81532999e..2482effe3 100644
--- a/pkg/util/camel/camel_runtime_catalog.go
+++ b/pkg/util/camel/camel_runtime_catalog.go
@@ -28,6 +28,7 @@ func NewRuntimeCatalog(spec v1.CamelCatalogSpec) *RuntimeCatalog {
 	catalog := RuntimeCatalog{}
 	catalog.CamelCatalogSpec = spec
 	catalog.artifactByScheme = make(map[string]string)
+	catalog.artifactByDataFormat = make(map[string]string)
 	catalog.schemesByID = make(map[string]v1.CamelScheme)
 	catalog.languageDependencies = make(map[string]string)
 	catalog.javaTypeDependencies = make(map[string]string)
@@ -38,6 +39,10 @@ func NewRuntimeCatalog(spec v1.CamelCatalogSpec) *RuntimeCatalog {
 			catalog.artifactByScheme[scheme.ID] = id
 			catalog.schemesByID[scheme.ID] = scheme
 		}
+		for _, dataFormat := range artifact.DataFormats {
+			dataFormat := dataFormat
+			catalog.artifactByDataFormat[dataFormat] = id
+		}
 		for _, language := range artifact.Languages {
 			// Skip languages in common dependencies since they are always available to integrations
 			if artifact.ArtifactID != "camel-base" {
@@ -60,6 +65,7 @@ type RuntimeCatalog struct {
 	v1.CamelCatalogSpec
 
 	artifactByScheme     map[string]string
+	artifactByDataFormat map[string]string
 	schemesByID          map[string]v1.CamelScheme
 	languageDependencies map[string]string
 	javaTypeDependencies map[string]string
@@ -86,6 +92,16 @@ func (c *RuntimeCatalog) GetArtifactByScheme(scheme string) *v1.CamelArtifact {
 	return nil
 }
 
+// GetArtifactByDataFormat returns the artifact corresponding to the given data format
+func (c *RuntimeCatalog) GetArtifactByDataFormat(dataFormat string) *v1.CamelArtifact {
+	if id, ok := c.artifactByDataFormat[dataFormat]; ok {
+		if artifact, present := c.Artifacts[id]; present {
+			return &artifact
+		}
+	}
+	return nil
+}
+
 // GetScheme returns the scheme definition for the given scheme id
 func (c *RuntimeCatalog) GetScheme(id string) (v1.CamelScheme, bool) {
 	scheme, ok := c.schemesByID[id]
diff --git a/pkg/util/source/inspector.go b/pkg/util/source/inspector.go
index 94b89510e..90a005171 100644
--- a/pkg/util/source/inspector.go
+++ b/pkg/util/source/inspector.go
@@ -29,6 +29,10 @@ import (
 
 type catalog2deps func(*camel.RuntimeCatalog) []string
 
+const (
+	defaultJsonDataformat = "json-jackson"
+)
+
 var (
 	singleQuotedFrom        = regexp.MustCompile(`from\s*\(\s*'([a-zA-Z0-9-]+:[^']+)'`)
 	doubleQuotedFrom        = regexp.MustCompile(`from\s*\(\s*"([a-zA-Z0-9-]+:[^"]+)"`)
@@ -59,11 +63,19 @@ var (
 	}
 
 	sourceDependencies = map[*regexp.Regexp]catalog2deps{
-		jsonLibraryRegexp: func(_ *camel.RuntimeCatalog) []string {
-			return []string{"camel:jackson"}
+		jsonLibraryRegexp: func(catalog *camel.RuntimeCatalog) []string {
+			res := make([]string, 0)
+			if jsonDF := catalog.GetArtifactByDataFormat(defaultJsonDataformat); jsonDF != nil {
+				res = append(res, jsonDF.GetDependencyID())
+			}
+			return res
 		},
-		jsonLanguageRegexp: func(_ *camel.RuntimeCatalog) []string {
-			return []string{"camel:jackson"}
+		jsonLanguageRegexp: func(catalog *camel.RuntimeCatalog) []string {
+			res := make([]string, 0)
+			if jsonDF := catalog.GetArtifactByDataFormat(defaultJsonDataformat); jsonDF != nil {
+				res = append(res, jsonDF.GetDependencyID())
+			}
+			return res
 		},
 		restConfigurationRegexp: func(catalog *camel.RuntimeCatalog) []string {
 			deps := make([]string, 0)
diff --git a/pkg/util/source/inspector_yaml.go b/pkg/util/source/inspector_yaml.go
index a981049f3..bcfa6cf4a 100644
--- a/pkg/util/source/inspector_yaml.go
+++ b/pkg/util/source/inspector_yaml.go
@@ -19,6 +19,7 @@ package source
 
 import (
 	"fmt"
+	"strings"
 
 	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
 	yaml2 "gopkg.in/yaml.v2"
@@ -61,6 +62,22 @@ func (i YAMLInspector) parseStep(key string, content interface{}, meta *Metadata
 		meta.RequiredCapabilities.Add(v1.CapabilityRest)
 	case "circuitBreaker":
 		meta.RequiredCapabilities.Add(v1.CapabilityCircuitBreaker)
+	case "unmarshal":
+		fallthrough
+	case "marshal":
+		if cm, ok := content.(map[interface{}]interface{}); ok {
+			if js, jsOk := cm["json"]; jsOk {
+				dataFormatID := defaultJsonDataformat
+				if jsContent, jsContentOk := js.(map[interface{}]interface{}); jsContentOk {
+					if lib, libOk := jsContent["library"]; libOk {
+						dataFormatID = strings.ToLower(fmt.Sprintf("json-%s", lib))
+					}
+				}
+				if dfDep := i.catalog.GetArtifactByDataFormat(dataFormatID); dfDep != nil {
+					i.addDependency(dfDep.GetDependencyID(), meta)
+				}
+			}
+		}
 	}
 
 	var maybeURI string


Test patch:
diff --git a/pkg/cmd/local_run_test.go b/pkg/cmd/local_run_test.go
new file mode 100644
index 000000000..72f84f232
--- /dev/null
+++ b/pkg/cmd/local_run_test.go
@@ -0,0 +1,78 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cmd
+
+import (
+	"testing"
+
+	"github.com/apache/camel-k/pkg/util/test"
+	"github.com/spf13/cobra"
+)
+
+func addTestLocalRunCmd(rootCmdOptions *RootCmdOptions, rootCmd *cobra.Command) *localRunCmdOptions {
+	//add a testing version of run Command
+	localCmd := newCmdLocal(rootCmdOptions)
+	localRunCmd, localRunCmdOptions := newCmdLocalRun(rootCmdOptions)
+	localRunCmd.RunE = func(c *cobra.Command, args []string) error {
+		return nil
+	}
+	localRunCmd.Args = test.ArbitraryArgs
+	localCmd.AddCommand(localRunCmd)
+	rootCmd.AddCommand(localCmd)
+	return localRunCmdOptions
+}
+
+func TestLocalRunPropertyFileFlag(t *testing.T) {
+	options, rootCmd := kamelTestPreAddCommandInit()
+
+	localRunCmdOptions := addTestLocalRunCmd(options, rootCmd)
+
+	kamelTestPostAddCommandInit(t, rootCmd)
+
+	_, err := test.ExecuteCommand(rootCmd, "local", "run", "route.java", "--property-file", "file1.properties", "--property-file", "file2.properties")
+	if err != nil {
+		t.Fatalf("Unexpected error: %v", err)
+	}
+
+	if len(localRunCmdOptions.PropertyFiles) != 2 {
+		t.Fatalf("Property files expected to contain: \n %v elements\nGot:\n %v elements\n", 2, len(localRunCmdOptions.PropertyFiles))
+	}
+	if localRunCmdOptions.PropertyFiles[0] != "file1.properties" || localRunCmdOptions.PropertyFiles[1] != "file2.properties" {
+		t.Fatalf("Property files expected to be: \n %v\nGot:\n %v\n", "[file1.properties, file2.properties]", localRunCmdOptions.PropertyFiles)
+	}
+}
+
+func TestLocalRunAdditionalDependenciesFlag(t *testing.T) {
+	options, rootCmd := kamelTestPreAddCommandInit()
+
+	localRunCmdOptions := addTestLocalRunCmd(options, rootCmd)
+
+	kamelTestPostAddCommandInit(t, rootCmd)
+
+	_, err := test.ExecuteCommand(rootCmd, "local", "run", "route.java", "-d", "mvn:camel-component-1", "-d", "mvn:camel-component-2")
+	if err != nil {
+		t.Fatalf("Unexpected error: %v", err)
+	}
+
+	if len(localRunCmdOptions.AdditionalDependencies) != 2 {
+		t.Fatalf("Additional dependencies expected to contain: \n %v elements\nGot:\n %v elements\n", 2, len(localRunCmdOptions.AdditionalDependencies))
+	}
+	if localRunCmdOptions.AdditionalDependencies[0] != "mvn:camel-component-1" || localRunCmdOptions.AdditionalDependencies[1] != "mvn:camel-component-2" {
+		t.Fatalf("Additional dependencies expected to be: \n %v\nGot:\n %v\n", "[mvn:camel-component-1, mvn:camel-component-2]", localRunCmdOptions.AdditionalDependencies)
+	}
+}
diff --git a/pkg/util/source/inspector_yaml_test.go b/pkg/util/source/inspector_yaml_test.go
index c7ed02427..518b93816 100644
--- a/pkg/util/source/inspector_yaml_test.go
+++ b/pkg/util/source/inspector_yaml_test.go
@@ -18,6 +18,7 @@ limitations under the License.
 package source
 
 import (
+	"fmt"
 	"testing"
 
 	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
@@ -104,3 +105,81 @@ func TestYAMLRestDSL(t *testing.T) {
 		})
 	}
 }
+
+const YAMLJSONMarshal = `
+- from:
+    uri: timer:tick
+    steps:
+    - marshal:
+        json: {}
+`
+
+const YAMLJSONUnmarshal = `
+- from:
+    uri: timer:tick
+    steps:
+    - unmarshal:
+        json: {}
+`
+
+const YAMLJSONGsonMarshal = `
+- from:
+    uri: timer:tick
+    steps:
+    - marshal:
+        json:
+          library: Gson
+`
+
+const YAMLJSONUnknownMarshal = `
+- from:
+    uri: timer:tick
+    steps:
+    - marshal:
+        json:
+          library: Unknown
+`
+
+func TestYAMLJson(t *testing.T) {
+	tc := []struct {
+		source     string
+		dependency string
+	}{
+		{
+			source:     YAMLJSONMarshal,
+			dependency: "camel-quarkus:jackson",
+		},
+		{
+			source:     YAMLJSONUnmarshal,
+			dependency: "camel-quarkus:jackson",
+		},
+		{
+			source:     YAMLJSONGsonMarshal,
+			dependency: "camel-quarkus:gson",
+		},
+		{
+			source:     YAMLJSONUnknownMarshal,
+			dependency: "camel-quarkus:timer",
+		},
+	}
+
+	for i, test := range tc {
+		t.Run(fmt.Sprintf("%s-%d", test.dependency, i), func(t *testing.T) {
+			code := v1.SourceSpec{
+				DataSpec: v1.DataSpec{
+					Name:    "route.yaml",
+					Content: test.source,
+				},
+				Language: v1.LanguageYaml,
+			}
+
+			meta := NewMetadata()
+			inspector := NewtestYAMLInspector(t)
+
+			err := inspector.Extract(code, &meta)
+			assert.Nil(t, err)
+			assert.True(t, meta.RequiredCapabilities.IsEmpty())
+			assert.Contains(t, meta.Dependencies.List(), test.dependency)
+		})
+	}
+}