diff --git a/circle/circle/settings/base.py b/circle/circle/settings/base.py
index b6be665..ab3c8e4 100644
--- a/circle/circle/settings/base.py
+++ b/circle/circle/settings/base.py
@@ -282,6 +282,7 @@ TEMPLATE_CONTEXT_PROCESSORS = (
     'django.core.context_processors.request',
     'dashboard.context_processors.notifications',
     'dashboard.context_processors.extract_settings',
+    'dashboard.context_processors.broadcast_messages',
 )
 
 # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-dirs
diff --git a/circle/dashboard/admin.py b/circle/dashboard/admin.py
index ac798ca..f2baf13 100644
--- a/circle/dashboard/admin.py
+++ b/circle/dashboard/admin.py
@@ -21,7 +21,7 @@ from django import contrib
 from django.contrib.auth.admin import UserAdmin, GroupAdmin
 from django.contrib.auth.models import User, Group
 
-from dashboard.models import Profile, GroupProfile, ConnectCommand
+from dashboard.models import Profile, GroupProfile, ConnectCommand, Message
 
 
 class ProfileInline(contrib.admin.TabularInline):
@@ -43,3 +43,5 @@ contrib.admin.site.unregister(User)
 contrib.admin.site.register(User, UserAdmin)
 contrib.admin.site.unregister(Group)
 contrib.admin.site.register(Group, GroupAdmin)
+
+contrib.admin.site.register(Message)
diff --git a/circle/dashboard/context_processors.py b/circle/dashboard/context_processors.py
index 3777694..dff324a 100644
--- a/circle/dashboard/context_processors.py
+++ b/circle/dashboard/context_processors.py
@@ -16,6 +16,10 @@
 # with CIRCLE.  If not, see <http://www.gnu.org/licenses/>.
 
 from django.conf import settings
+from django.db.models import Q
+from django.utils import timezone
+
+from .models import Message
 
 
 def notifications(request):
@@ -31,3 +35,10 @@ def extract_settings(request):
         'COMPANY_NAME': getattr(settings, "COMPANY_NAME", None),
         'ADMIN_ENABLED': getattr(settings, "ADMIN_ENABLED", False),
     }
+
+
+def broadcast_messages(request):
+    now = timezone.now()
+    messages = Message.objects.filter(enabled=True).exclude(
+        Q(starts_at__gt=now) | Q(ends_at__lt=now))
+    return {'broadcast_messages': messages}
diff --git a/circle/dashboard/forms.py b/circle/dashboard/forms.py
index db5a462..43d619e 100644
--- a/circle/dashboard/forms.py
+++ b/circle/dashboard/forms.py
@@ -57,7 +57,7 @@ from vm.models import (
 from storage.models import DataStore, Disk
 from django.contrib.admin.widgets import FilteredSelectMultiple
 from django.contrib.auth.models import Permission
-from .models import Profile, GroupProfile
+from .models import Profile, GroupProfile, Message
 from circle.settings.base import LANGUAGES, MAX_NODE_RAM
 from django.utils.translation import string_concat
 
@@ -1624,3 +1624,15 @@ class DiskForm(ModelForm):
         model = Disk
         fields = ("name", "filename", "datastore", "type", "bus", "size",
                   "base", "dev_num", "destroyed", "is_ready", )
+
+
+class MessageForm(ModelForm):
+    class Meta:
+        model = Message
+        fields = ("message", "enabled", "effect", "starts_at", "ends_at")
+
+    @property
+    def helper(self):
+        helper = FormHelper()
+        helper.add_input(Submit("submit", _("Save")))
+        return helper
diff --git a/circle/dashboard/migrations/0003_message.py b/circle/dashboard/migrations/0003_message.py
new file mode 100644
index 0000000..4c8aa47
--- /dev/null
+++ b/circle/dashboard/migrations/0003_message.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+import django.utils.timezone
+import model_utils.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dashboard', '0002_auto_20150318_1317'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Message',
+            fields=[
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)),
+                ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)),
+                ('message', models.CharField(max_length=500, verbose_name='message')),
+                ('starts_at', models.DateTimeField(null=True, verbose_name='starts at', blank=True)),
+                ('ends_at', models.DateTimeField(null=True, verbose_name='ends at', blank=True)),
+                ('effect', models.CharField(default=b'info', max_length=10, verbose_name='effect', choices=[(b'success', 'success'), (b'info', 'info'), (b'warning', 'warning'), (b'danger', 'danger')])),
+                ('enabled', models.BooleanField(default=False, verbose_name='enabled')),
+            ],
+            options={
+                'ordering': ['-ends_at'],
+            },
+            bases=(models.Model,),
+        ),
+    ]
diff --git a/circle/dashboard/models.py b/circle/dashboard/models.py
index 2a5ac3f..5690704 100644
--- a/circle/dashboard/models.py
+++ b/circle/dashboard/models.py
@@ -59,6 +59,31 @@ def pwgen():
     return User.objects.make_random_password()
 
 
+class Message(TimeStampedModel):
+    message = CharField(max_length=500, verbose_name=_('message'))
+    starts_at = DateTimeField(
+        null=True, blank=True, verbose_name=_('starts at'))
+    ends_at = DateTimeField(
+        null=True, blank=True, verbose_name=_('ends at'))
+    effect = CharField(
+        default='info', max_length=10, verbose_name=_('effect'),
+        choices=(('success', _('success')), ('info', _('info')),
+                 ('warning', _('warning')), ('danger', _('danger'))))
+    enabled = BooleanField(default=False, verbose_name=_('enabled'))
+
+    class Meta:
+        ordering = ["id"]
+        verbose_name = _('message')
+        verbose_name_plural = _('messages')
+
+    def __unicode__(self):
+        return self.message
+
+    @permalink
+    def get_absolute_url(self):
+        return ('dashboard.views.message-detail', None, {'pk': self.pk})
+
+
 class Favourite(Model):
     instance = ForeignKey("vm.Instance")
     user = ForeignKey(User)
diff --git a/circle/dashboard/static/dashboard/dashboard.js b/circle/dashboard/static/dashboard/dashboard.js
index e466f70..9d16f6f 100644
--- a/circle/dashboard/static/dashboard/dashboard.js
+++ b/circle/dashboard/static/dashboard/dashboard.js
@@ -527,3 +527,22 @@ function replaceTag(tag) {
 function safe_tags_replace(str) {
     return str.replace(/[&<>]/g, replaceTag);
 }
+
+$(function () {
+  var closed = JSON.parse(getCookie('broadcast-messages'));
+  $('.broadcast-message').each(function() {
+    var id = $(this).data('id');
+    if (closed && closed.indexOf(id) != -1) {
+      $(this).remove()
+    }
+  });
+
+  $('.broadcast-message').on('closed.bs.alert', function () {
+    var closed = JSON.parse(getCookie('broadcast-messages'));
+    if (!closed) {
+      closed = [];
+    }
+    closed.push($(this).data('id'));
+    setCookie('broadcast-messages', JSON.stringify(closed), 7 * 24 * 60 * 60 * 1000, "/");
+  });
+});
diff --git a/circle/dashboard/static/dashboard/dashboard.less b/circle/dashboard/static/dashboard/dashboard.less
index e191027..923ee29 100644
--- a/circle/dashboard/static/dashboard/dashboard.less
+++ b/circle/dashboard/static/dashboard/dashboard.less
@@ -1315,3 +1315,9 @@ textarea[name="new_members"] {
 .little-margin-bottom {
   margin-bottom: 5px;
 }
+
+.broadcast-message {
+  margin-bottom: 5px;
+  padding-top: 5px;
+  padding-bottom: 5px;
+}
diff --git a/circle/dashboard/tables.py b/circle/dashboard/tables.py
index b5059cd..b36aaf0 100644
--- a/circle/dashboard/tables.py
+++ b/circle/dashboard/tables.py
@@ -29,7 +29,7 @@ from django_sshkey.models import UserKey
 
 from storage.models import Disk
 from vm.models import Node, InstanceTemplate, Lease
-from dashboard.models import ConnectCommand
+from dashboard.models import ConnectCommand, Message
 
 
 class FileSizeColumn(Column):
@@ -354,3 +354,18 @@ class DiskListTable(Table):
         order_by = ("-pk", )
         per_page = 15
         empty_text = _("No disk found.")
+
+
+class MessageListTable(Table):
+    message = LinkColumn(
+        'dashboard.views.message-detail',
+        args=[A('pk')],
+        attrs={'th': {'data-sort': "string"}}
+    )
+
+    class Meta:
+        model = Message
+        attrs = {'class': "table table-bordered table-striped table-hover",
+                 'id': "disk-list-table"}
+        order_by = ("-pk", )
+        fields = ('pk', 'message', 'enabled', 'effect')
diff --git a/circle/dashboard/templates/base.html b/circle/dashboard/templates/base.html
index b3bf340..30c500e 100644
--- a/circle/dashboard/templates/base.html
+++ b/circle/dashboard/templates/base.html
@@ -1,5 +1,6 @@
 {% load i18n %}
 {% load staticfiles %}
+{% load cache %}
 {% load compressed %}
 <!DOCTYPE html>
 <html lang="{{lang}}">
@@ -40,6 +41,22 @@
     </div><!-- navbar navbar-inverse navbar-fixed-top -->
 
     <div class="container">
+      {% block broadcast_messages %}
+        {% cache 1 broadcast_messages %}
+          <div id="broadcast-messages">
+            {% for message in broadcast_messages %}
+              <div data-id={{ message.id }} class="alert alert-{{ message.effect }}
+                  text-center broadcast-message">
+                <button type="button" class="close" data-dismiss="alert" aria-label="Close">
+                  <span aria-hidden="true">&times;</span>
+                </button>
+                {{ message.message|safe }}
+              </div>
+            {% endfor %}
+          </div>
+        {% endcache %}
+      {% endblock broadcast_messages %}
+
       {% block messages %}
         <div class="messagelist">
         {% if messages %}
diff --git a/circle/dashboard/templates/dashboard/base.html b/circle/dashboard/templates/dashboard/base.html
index 855265e..7466c6e 100644
--- a/circle/dashboard/templates/dashboard/base.html
+++ b/circle/dashboard/templates/dashboard/base.html
@@ -27,6 +27,12 @@
         <span class="hidden-sm">{% trans "Admin" %}</span>
       </a>
     </li>
+    <li>
+      <a href="{% url "dashboard.views.message-list" %}">
+        <i class="fa fa-bullhorn"></i>
+        <span class="hidden-sm">{% trans "Messages" %}</span>
+      </a>
+    </li>
     {% endif %}
     <li>
       <a href="{% url "dashboard.views.storage" %}">
diff --git a/circle/dashboard/templates/dashboard/message-create.html b/circle/dashboard/templates/dashboard/message-create.html
new file mode 100644
index 0000000..4a3f199
--- /dev/null
+++ b/circle/dashboard/templates/dashboard/message-create.html
@@ -0,0 +1,27 @@
+{% extends "dashboard/base.html" %}
+{% load i18n %}
+{% load crispy_forms_tags %}
+
+{% block title-page %}{% trans "Broadcast Messages" %}{% endblock %}
+
+{% block content %}
+
+<div class="row">
+  <div class="col-md-12">
+    <div class="panel panel-default">
+      <div class="panel-heading">
+        <a href="{% url "dashboard.views.message-list" %}" class="btn btn-default btn-xs pull-right">
+          {% trans "Back" %}
+        </a>
+        <h3 class="no-margin">
+          <i class="fa fa-bullhorn"></i>
+          {% trans "New message" %}
+        </h3>
+      </div>
+      <div class="panel-body">
+        {% crispy form %}
+      </div><!-- .panel-body -->
+    </div>
+  </div>
+</div>
+{% endblock %}
diff --git a/circle/dashboard/templates/dashboard/message-edit.html b/circle/dashboard/templates/dashboard/message-edit.html
new file mode 100644
index 0000000..ea6b2a7
--- /dev/null
+++ b/circle/dashboard/templates/dashboard/message-edit.html
@@ -0,0 +1,34 @@
+{% extends "dashboard/base.html" %}
+{% load i18n %}
+{% load crispy_forms_tags %}
+
+{% block title-page %}{% trans "Broadcast Messages" %}{% endblock %}
+
+{% block content %}
+
+<div class="row">
+  <div class="col-md-12">
+    <div class="panel panel-default">
+      <div class="panel-heading">
+        <div class="pull-right">
+          <a href="{% url "dashboard.views.message-list" %}"
+            class="btn btn-default btn-xs">
+            {% trans "Back" %}
+          </a>
+          <a href="{% url "dashboard.views.message-delete" pk=object.pk %}"
+             class="btn btn-danger btn-xs">
+            {% trans "Delete" %}
+          </a>
+        </div>
+        <h3 class="no-margin">
+          <i class="fa fa-bullhorn"></i>
+          {% trans "Edit message" %}
+        </h3>
+      </div>
+      <div class="panel-body">
+        {% crispy form %}
+      </div><!-- .panel-body -->
+    </div>
+  </div>
+</div>
+{% endblock %}
diff --git a/circle/dashboard/templates/dashboard/message-list.html b/circle/dashboard/templates/dashboard/message-list.html
new file mode 100644
index 0000000..a3834a5
--- /dev/null
+++ b/circle/dashboard/templates/dashboard/message-list.html
@@ -0,0 +1,28 @@
+{% extends "dashboard/base.html" %}
+{% load staticfiles %}
+{% load i18n %}
+{% load render_table from django_tables2 %}
+
+{% block title-page %}{% trans "Broadcast Messages" %}{% endblock %}
+
+{% block content %}
+
+<div class="row">
+  <div class="col-md-12">
+    <div class="panel panel-default">
+      <div class="panel-heading">
+        <a href="{% url "dashboard.views.message-create" %}" class="pull-right btn btn-success btn-xs">
+          <i class="fa fa-plus"></i> {% trans "new message" %}
+        </a>
+        <h3 class="no-margin"><i class="fa fa-bullhorn"></i> {% trans "Broadcast Messages" %}</h3>
+      </div>
+      <div class="panel-body">
+        <div class="table-responsive">
+          {% render_table table %}
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+
+{% endblock %}
diff --git a/circle/dashboard/urls.py b/circle/dashboard/urls.py
index edefaeb..11d2f11 100644
--- a/circle/dashboard/urls.py
+++ b/circle/dashboard/urls.py
@@ -54,6 +54,7 @@ from .views import (
     NodeActivityView,
     UserList,
     StorageDetail, DiskDetail,
+    MessageList, MessageDetail, MessageCreate, MessageDelete,
 )
 from .views.vm import vm_ops, vm_mass_ops
 from .views.node import node_ops
@@ -232,6 +233,15 @@ urlpatterns = patterns(
         name="dashboard.views.storage"),
     url(r'^disk/(?P<pk>\d+)/$', DiskDetail.as_view(),
         name="dashboard.views.disk-detail"),
+
+    url(r'^message/list/$', MessageList.as_view(),
+        name="dashboard.views.message-list"),
+    url(r'^message/(?P<pk>\d+)/$', MessageDetail.as_view(),
+        name="dashboard.views.message-detail"),
+    url(r'^message/create/$', MessageCreate.as_view(),
+        name="dashboard.views.message-create"),
+    url(r'^message/delete/(?P<pk>\d+)/$', MessageDelete.as_view(),
+        name="dashboard.views.message-delete"),
 )
 
 urlpatterns += patterns(
diff --git a/circle/dashboard/views/__init__.py b/circle/dashboard/views/__init__.py
index 64cc7d4..f799d8d 100644
--- a/circle/dashboard/views/__init__.py
+++ b/circle/dashboard/views/__init__.py
@@ -14,3 +14,4 @@ from vm import *
 from graph import *
 from storage import *
 from request import *
+from message import *
diff --git a/circle/dashboard/views/message.py b/circle/dashboard/views/message.py
new file mode 100644
index 0000000..8310eac
--- /dev/null
+++ b/circle/dashboard/views/message.py
@@ -0,0 +1,41 @@
+from django.contrib.messages.views import SuccessMessageMixin
+from django.core.urlresolvers import reverse
+from django.utils.translation import ugettext as _
+from django.views.generic import CreateView, DeleteView, UpdateView
+
+from braces.views import SuperuserRequiredMixin, LoginRequiredMixin
+from django_tables2 import SingleTableView
+
+from ..forms import MessageForm
+from ..models import Message
+from ..tables import MessageListTable
+
+
+class MessageList(LoginRequiredMixin, SuperuserRequiredMixin, SingleTableView):
+    template_name = "dashboard/message-list.html"
+    model = Message
+    table_class = MessageListTable
+
+
+class MessageDetail(LoginRequiredMixin, SuperuserRequiredMixin,
+                    SuccessMessageMixin, UpdateView):
+    model = Message
+    template_name = "dashboard/message-edit.html"
+    form_class = MessageForm
+    success_message = _("Broadcast message successfully updated.")
+
+
+class MessageCreate(LoginRequiredMixin, SuperuserRequiredMixin,
+                    SuccessMessageMixin, CreateView):
+    model = Message
+    template_name = "dashboard/message-create.html"
+    form_class = MessageForm
+    success_message = _("New broadcast message successfully created.")
+
+
+class MessageDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
+    model = Message
+    template_name = "dashboard/confirm/base-delete.html"
+
+    def get_success_url(self):
+        return reverse("dashboard.views.message-list")