diff --git a/release_dashboard/static/release_dashboard/js/hotfix_release.js b/release_dashboard/static/release_dashboard/js/hotfix_release.js
new file mode 100644
index 0000000..630ce15
--- /dev/null
+++ b/release_dashboard/static/release_dashboard/js/hotfix_release.js
@@ -0,0 +1,70 @@
+/**
+ *
+ *
+ */
+$( "button.hotfix" ).click( function( e ) {
+
+  // don't send the form
+  e.preventDefault();
+  var button = $( this );
+  var id = button.attr( "id" ).replace( "hotfix_", "" );
+  var repo = id;
+  var span = $( "span#hotfix_error_" + id );
+  var links = $( "#links_" + id );
+  var push = $( "select#push_" + id + " option:selected" ).val();
+  var empty = $( "input#empty_" + id ).prop( "checked" );
+
+  $.ajax( {
+    url: repo + "/",
+    type: "POST",
+    data: JSON.stringify( { push: push, empty: empty } ),
+    contentType: "application/json; charset=utf-8",
+    dataType: "json",
+    success: successFunc,
+    error: errorFunc,
+    /* eslint-disable-next-line no-undef */ // at csrf.js
+    beforeSend: csrftokenFunc
+  } );
+
+  button.attr( "disabled", "disabled" );
+  span.html( "processing" );
+  span.show();
+  links.addClass( "hidden" );
+
+  function successFunc( data, _status ) {
+    span.html( "" );
+    $( "#link_done_" + id ).attr( "href", data.urls[ 0 ] );
+    $( "#link_latest_" + id ).attr( "href", data.urls[ 1 ] );
+    links.removeClass( "hidden" );
+    button.removeAttr( "disabled" );
+  }
+
+  function errorFunc( _jqXHR, _status, error ) {
+    span.html( error );
+    button.removeAttr( "disabled" );
+  }
+} );
+
+$( "td.version > select" ).change( function() {
+  var id = $( this ).attr( "id" ).replace( "version_", "" );
+  var version = $( this ).val();
+  var button = $( "button#hotfix_" + id );
+  var span = $( "span#hotfix_error_" + id );
+  var links = $( "#links_" + id );
+
+  if ( version.match( /^branch\/mr[0-9]+\.[0-9]+\.[0-9]+$/ ) ) {
+    button.html( "Release hotfix" );
+    button.removeAttr( "disabled" );
+  } else {
+    button.html( "Select branch to hotfix" );
+    button.attr( "disabled", "disabled" );
+  }
+  span.html( "" );
+  links.addClass( "hidden" );
+} );
+
+$( document ).ready( function() {
+  $( "td.version > select option[value^=\"branch/mr\"]" ).each( function() {
+ $( this ).change();
+} );
+} );
diff --git a/release_dashboard/templates/release_dashboard/build_release.html b/release_dashboard/templates/release_dashboard/build_release.html
index 0c7cab5..9719fe5 100644
--- a/release_dashboard/templates/release_dashboard/build_release.html
+++ b/release_dashboard/templates/release_dashboard/build_release.html
@@ -61,13 +61,19 @@
     </div>
       <div class="panel panel-default">
         <div class="panel-heading">
-            <h3 class="panel-title">Projects to build</h3>
+          <div class="row">
+            <h3 class="col-xs-10 panel-title">Projects to build</h3>
+            <div class="col-xs-1 hidden" id="hotfix">
+              <a href="{% url 'release_dashboard:hotfix_release' config.release %}" class="btn btn-primary right">Hotfixes</a>
+            </div>
+          </div>
         </div>
         <div class="panel-body">
             <ul class="list-group" style="column-count: 4;">
               {% for prj in config.projects %}
                 <li class="list-group-item {% if prj in build_deps %}list-group-item-warning{% endif %}">
                   <a href="{{ gerrit_url }}{{ prj }}.git;a=shortlog;h=refs/heads/{{ config.branch }}">{{ prj }}</a>
+                  {% if prj in build_deps %}<div class="hidden-xs pull-right label label-warning">dependency</div>{% endif %}
                 </li>
               {% endfor %}
             </ul>
@@ -84,6 +90,7 @@ $( document ).ready(function() {
   if (builds > 0 && release.match(/^release-mr[0-9]+\.[0-9]+\.[0-9]+$/)) {
     $( "#build_button" ).attr("disabled", "disabled");
     console.debug("mrX.X.X release, can't be built twice");
+    $( "#hotfix" ).toggleClass("hidden");
   }
 });
 
@@ -98,6 +105,7 @@ function delete_build_release( id ) {
     $("#br_" + id).remove();
     if ( $(".build_release").length === 0 ) {
       $( "#build_button" ).removeAttr("disabled");
+      $( "#hotfix" ).toggleClass("hidden");
     }
   }
 
diff --git a/release_dashboard/templates/release_dashboard/hotfix_release.html b/release_dashboard/templates/release_dashboard/hotfix_release.html
new file mode 100644
index 0000000..11b5153
--- /dev/null
+++ b/release_dashboard/templates/release_dashboard/hotfix_release.html
@@ -0,0 +1,66 @@
+{% extends "release_dashboard/base.html" %}
+{% load static %}
+{% block title %}Release Dashboard{% endblock %}
+{% block navlist %}
+<li><a href="{% url 'release_dashboard:index'%}">Release Dashboard</a></li>
+<li><a href="{% url 'release_dashboard:build_index'%}">Build Release</a></li>
+<li><a href="{% url 'release_dashboard:build_release' config.release %}">{{ config.release }}</a></li>
+{% endblock %}
+{% block content %}
+<div class="container">
+    <table class="table table-condensed">
+        <thead>
+            <tr>
+                <th>Project</th>
+                <th>Version</th>
+                <th>Push</th>
+                <th>Empty</th>
+                <th></th>
+                <th></th>
+            </tr>
+        </thead>
+        <tbody>
+        <form class="form-inline">{% csrf_token %}</form>
+      {% for p in config.projects %}
+          <tr class="repo">
+            <th><label for="version_{{ p }}">{{ p }}</label></th>
+            <td class="version">
+                <select class="form-control" id="version_{{ p }}" name="version_{{ p }}">
+                    <option value="ignore">ignore</option>
+                    <option value="branch/{{ config.branch }}">branch/{{ config.branch }}</option>
+                </select>
+            </td>
+            <td>
+              <select class="form-control" id="push_{{ p }}">
+                <option value="yes" selected="selected">yes</option>
+                <option value="no">no</option>
+              </select>
+            </td>
+            <td>
+              <input id="empty_{{ p }}" type="checkbox">
+            </td>
+            <td>
+              <button type="button" id="hotfix_{{ p }}" class="btn btn-warning hotfix" disabled="disabled">Select branch to hotfix</button>
+            </td>
+            <td>
+              <span class="text-danger" id="hotfix_error_{{ p }}"></span>
+              <div id="links_{{ p }}" class="hidden">
+                <div class="btn-group btn-group-sm" role="group" aria-label="grp1">
+                    <a class="btn btn-default btn-success" type="button" role="group" id="link_done_{{ p }}">Done</a>
+                </div>
+                <div class="btn-group btn-group-sm" role="group" aria-label="grp2">
+                    <a class="btn btn-default btn-info" type="button" role="group" id="link_latest_{{ p }}">latest Build</a>
+                </div>
+              </div>
+            </td>
+          </tr>
+      {% endfor %}
+        </tbody>
+    </table>
+    </div>
+</div>
+{% endblock %}
+{% block extrajs %}
+<script src="{% static 'release_dashboard/js/csrf.js' %}"></script>
+<script src="{% static 'release_dashboard/js/hotfix_release.js' %}"></script>
+{% endblock %}
\ No newline at end of file
diff --git a/release_dashboard/test/test_views.py b/release_dashboard/test/test_views.py
index 5b94a01..080510f 100644
--- a/release_dashboard/test/test_views.py
+++ b/release_dashboard/test/test_views.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2020 The Sipwise Team - http://sipwise.com
+# Copyright (C) 2020-2022 The Sipwise Team - http://sipwise.com
 #
 # This program is free software: you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by the Free
@@ -68,6 +68,68 @@ class TestHotfix(TestCase):
         self.assertEqual(res.context["projects"], expected)
 
 
+class TestHotfixRelease(TestCase):
+    def test_no_login(self):
+        res = self.client.get(
+            reverse(
+                "release_dashboard:hotfix_release", args=["release-mr7.5.2"]
+            )
+        )
+        self.assertNotEqual(res.status_code, 200)
+
+    def test_login_ok(self):
+        user = User.objects.create_user(username="test")
+        self.client.force_login(user)
+        res = self.client.get(
+            reverse(
+                "release_dashboard:hotfix_release", args=["release-mr7.5.2"]
+            )
+        )
+        self.assertEqual(res.status_code, 200)
+
+    def test_no_mrXXX(self):
+        user = User.objects.create_user(username="test")
+        self.client.force_login(user)
+        res = self.client.get(
+            reverse("release_dashboard:hotfix_release", args=["release-mr7.5"])
+        )
+        self.assertNotEqual(res.status_code, 200)
+
+        res = self.client.get(
+            reverse(
+                "release_dashboard:hotfix_release",
+                args=["release-trunk-buster"],
+            )
+        )
+        self.assertNotEqual(res.status_code, 200)
+
+    def test_project_ok(self):
+        user = User.objects.create_user(username="test")
+        self.client.force_login(user)
+        res = self.client.post(
+            reverse(
+                "release_dashboard:hotfix_release_build",
+                args=["release-mr7.5.2", "data-hal"],
+            ),
+            {"push": "no"},
+            content_type="application/json",
+        )
+        self.assertEqual(res.status_code, 200)
+
+    def test_project_wrong(self):
+        user = User.objects.create_user(username="test")
+        self.client.force_login(user)
+        res = self.client.post(
+            reverse(
+                "release_dashboard:hotfix_release_build",
+                args=["release-mr7.5.2", "fake-project"],
+            ),
+            {"push": "no"},
+            content_type="application/json",
+        )
+        self.assertNotEqual(res.status_code, 200)
+
+
 class TestDocker(TestCase):
     def test_no_login(self):
         res = self.client.get(reverse("release_dashboard:docker_images"))
@@ -81,17 +143,17 @@ class TestDocker(TestCase):
 
 
 class TestBuildRelease(BaseTest):
-    fixtures = ['test_build_release']
+    fixtures = ["test_build_release"]
 
     def test_no_login(self):
-        url = reverse("release_dashboard:build_release", args=['trunk'])
+        url = reverse("release_dashboard:build_release", args=["trunk"])
         res = self.client.get(url)
         self.assertNotEqual(res.status_code, 200)
 
     def test_login_ok(self):
         user = User.objects.create_user(username="test")
         self.client.force_login(user)
-        url = reverse("release_dashboard:build_release", args=['trunk'])
+        url = reverse("release_dashboard:build_release", args=["trunk"])
         res = self.client.get(url)
         self.assertEqual(res.status_code, 200)
 
@@ -99,10 +161,10 @@ class TestBuildRelease(BaseTest):
         user = User.objects.create_user(username="test")
         self.client.force_login(user)
         # no build yet
-        url = reverse("release_dashboard:build_release", args=['mr8.1'])
+        url = reverse("release_dashboard:build_release", args=["mr8.1"])
         res = self.client.get(url)
         self.assertEqual(res.status_code, 200)
-        self.assertTrue(res.context['done'])
+        self.assertTrue(res.context["done"])
 
     def test_context_not_done(self):
         user = User.objects.create_user(username="test")
@@ -112,7 +174,7 @@ class TestBuildRelease(BaseTest):
         )
         br.built_projects = None
         br.save()
-        url = reverse("release_dashboard:build_release", args=['trunk'])
+        url = reverse("release_dashboard:build_release", args=["trunk"])
         res = self.client.get(url)
         self.assertEqual(res.status_code, 200)
-        self.assertFalse(res.context['done'])
+        self.assertFalse(res.context["done"])
diff --git a/release_dashboard/urls.py b/release_dashboard/urls.py
index 0d97d98..a148746 100644
--- a/release_dashboard/urls.py
+++ b/release_dashboard/urls.py
@@ -27,6 +27,16 @@ urlpatterns = [
         build.build_release,
         name="build_release",
     ),
+    re_path(
+        r"^hotfix_release/(?P<release>[^/]+)/$",
+        build.hotfix_release,
+        name="hotfix_release",
+    ),
+    re_path(
+        r"^hotfix_release/(?P<release>[^/]+)/(?P<project>[^/]+)/$",
+        build.hotfix_release_build,
+        name="hotfix_release_build",
+    ),
     re_path(r"^hotfix/$", build.hotfix, name="hotfix"),
     re_path(
         r"^hotfix/(?P<branch>[^/]+)/(?P<project>[^/]+)/$", build.hotfix_build
diff --git a/release_dashboard/views/__init__.py b/release_dashboard/views/__init__.py
index e346beb..d1b7b76 100644
--- a/release_dashboard/views/__init__.py
+++ b/release_dashboard/views/__init__.py
@@ -17,10 +17,11 @@ import re
 from django.views.generic.base import TemplateView
 from natsort import humansorted
 
+from ..conf import settings
 from ..utils import get_branches
 from ..utils import get_tags
 
-regex_hotfix = re.compile(r"^mr[0-9]+\.[0-9]+\.[0-9]+$")
+regex_hotfix = re.compile(settings.RELEASE_DASHBOARD_FILTER_MRXXX)
 regex_mr = re.compile(r"^mr.+$")
 
 
diff --git a/release_dashboard/views/build.py b/release_dashboard/views/build.py
index 57bc79c..31c80aa 100644
--- a/release_dashboard/views/build.py
+++ b/release_dashboard/views/build.py
@@ -112,6 +112,44 @@ def hotfix(request):
     return render(request, "release_dashboard/hotfix.html", context)
 
 
+@login_required
+@require_http_methods(["POST"])
+def hotfix_release_build(request, release, project):
+    release_config = ReleaseConfig(release)
+    if project not in release_config.projects:
+        error = f"project:{project} not in {release_config.release}"
+        logger.error(error)
+        return HttpResponseNotFound(error)
+
+    if not regex_hotfix.match(release_config.branch):
+        error = f"branch:{release_config.branch} not valid. Not mrX.X.X format"
+        logger.error(error)
+        return HttpResponseNotFound(error)
+    logger.debug(body=request.body)
+    json_data = json.loads(request.body.decode("utf-8"))
+    push = json_data.get("push", "no")
+    empty = json_data.get("empty", False)
+    if push == "no":
+        logger.warn(f"dry-run for {project}:{release_config.branch}")
+    urls = build.trigger_hotfix(
+        project, release_config.branch, request.user, push, empty
+    )
+    return JsonResponse({"urls": urls})
+
+
+@login_required
+def hotfix_release(request, release):
+    release_config = ReleaseConfig(release)
+    if not regex_hotfix.match(release_config.branch):
+        error = (
+            "branch:%s not valid. Not mrX.X.X format" % release_config.branch
+        )
+        logger.error(error)
+        return HttpResponseNotFound(error)
+    context = {"config": release_config}
+    return render(request, "release_dashboard/hotfix_release.html", context)
+
+
 @login_required
 def refresh_all(request):
     if request.method == "POST":