diff --git a/src/tsuru_mdns.erl b/src/tsuru_mdns.erl
new file mode 100644
index 0000000000000000000000000000000000000000..9fecae10ff858ee2f1ee11e909d6b31b2b50d196
--- /dev/null
+++ b/src/tsuru_mdns.erl
@@ -0,0 +1,115 @@
+-module(tsuru_mdns).
+-author("erikh").
+
+-include_lib("kernel/src/inet_dns.hrl").
+
+-define(MDNS_ADDR, {224, 0, 0, 251}).
+-define(MDNS_PORT, 5353).
+
+%% API
+-export([publish_broker/3, discover_brokers/1, discover_brokers/2]).
+
+%% Exported types
+-export_type([brokers/0]).
+-type brokers() :: [{Address :: string(), Port :: inet:port_number()}].
+
+-spec(publish_broker(Domain :: string(), Address :: string(), Port :: inet:port_number()) ->
+    {ok, Pid :: pid()} |
+    {error, Reason :: inet:posix()}).
+publish_broker(Domain, Address, Port) ->
+    case gen_udp:open(?MDNS_PORT, [{active, false}, {reuseaddr, true}, {ip, ?MDNS_ADDR}, {multicast_ttl, 4}, {multicast_loop, false}, {mode, binary}, {add_membership, {?MDNS_ADDR, {0, 0, 0, 0}}}]) of
+        {ok, Socket} ->
+            Response = #dns_rec{
+                header = #dns_header{qr = 1, opcode = ?QUERY},
+                anlist = [
+                    #dns_rr{
+                        domain = Domain, type = ?S_SRV, data = {0, 0, Port, Address}
+                    }
+                ]
+            },
+            spawn_link(fun() -> receive_request(Domain, Socket, Response) end);
+        {error, Reason} ->
+            {error, Reason}
+    end.
+
+-spec(receive_request(Domain :: string(), Socket :: port(), Response :: #dns_rec{}) -> any()).
+receive_request(Domain, Socket, Response) ->
+    case gen_udp:recv(Socket, 1024) of
+        {ok, {Address, Port, Packet}} ->
+            handle_request(Domain, Address, Port, Packet, Response),
+            receive_request(Domain, Socket, Response);
+        {error, _Reason} ->
+            receive_request(Domain, Socket, Response)
+    end.
+
+-spec(handle_request(Domain :: string(), Address :: inet:ip_address(), Port :: inet:port_number(), Packet :: binary(), Response :: #dns_rec{}) -> ok).
+handle_request(Domain, Address, Port, Packet, Response) ->
+    case inet_dns:decode(Packet) of
+        {ok, Request} ->
+            case Request#dns_rec.qdlist of
+                [{dns_query, Domain, ptr, in}] ->
+                    {ok, Socket} = gen_udp:open(0),
+                    gen_udp:send(Socket, Address, Port, inet_dns:encode(Response)),
+                    gen_udp:close(Socket);
+                _Other ->
+                    ok
+            end;
+        _Error ->
+            ok
+    end.
+
+-spec(discover_brokers(Domain :: string()) ->
+    {ok, Brokers :: brokers()} |
+    {error, Reason :: not_owner | inet:posix()}).
+discover_brokers(Domain) ->
+    discover_brokers(Domain, 3000).
+
+-spec(discover_brokers(Domain :: string(), Timeout :: pos_integer()) ->
+    {ok, Brokers :: brokers()} |
+    {error, Reason :: not_owner | inet:posix()}).
+discover_brokers(Domain, Timeout) ->
+    case gen_udp:open(0, [{broadcast, true}, {active, false}, {mode, binary}]) of
+        {ok, Socket} ->
+            Request = #dns_rec{header = #dns_header{}, qdlist = [#dns_query{domain = Domain, type = ptr, class = in}]},
+            gen_udp:send(Socket, ?MDNS_ADDR, ?MDNS_PORT, inet_dns:encode(Request)),
+            receive_response(Domain, Socket, Timeout, []);
+        {error, Reason} ->
+            {error, Reason}
+    end.
+
+-spec(receive_response(Domain :: string(), Socket :: port(), Timeout :: pos_integer(), Brokers :: brokers()) ->
+    {ok, Brokers :: brokers()} |
+    {error, Reason :: not_owner | inet:posix()}).
+receive_response(Domain, Socket, Timeout, Brokers) ->
+    case gen_udp:recv(Socket, 1024, Timeout) of
+        {ok, {Address, Port, Packet}} ->
+            receive_response(Domain, Socket, Timeout, handle_response(Domain, Address, Port, Packet, Brokers));
+        {error, timeout} ->
+            {ok, Brokers};
+        {error, Reason} ->
+            {error, Reason}
+    end.
+
+-spec(handle_response(Domain :: string(), Address :: inet:ip_address(), Port :: inet:port_number(), Packet :: binary(), Brokers :: brokers()) -> Brokers :: brokers()).
+handle_response(Domain, Address, _Port, Packet, Brokers) ->
+    case inet_dns:decode(Packet) of
+        {ok, Request} ->
+            Header = Request#dns_rec.header,
+            if
+                Header#dns_header.qr ->
+                    case Request#dns_rec.anlist of
+                        [#dns_rr{domain = Domain, data = {0, 0, BrokerPort, "0.0.0.0"}}] ->
+                            Broker = {inet_parse:ntoa(Address), BrokerPort},
+                            [Broker | Brokers];
+                        [#dns_rr{domain = Domain, data = {0, 0, BrokerPort, BrokerAddress}}] ->
+                            Broker = {BrokerAddress, BrokerPort},
+                            [Broker | Brokers];
+                        _ ->
+                            Brokers
+                    end;
+                true ->
+                    Brokers
+            end;
+        {error, _Reason} ->
+            Brokers
+    end.
diff --git a/test/tsuru_mdns_test.erl b/test/tsuru_mdns_test.erl
new file mode 100644
index 0000000000000000000000000000000000000000..fe965a313652c4b4aed63ee93b7e8d21ef6b98c8
--- /dev/null
+++ b/test/tsuru_mdns_test.erl
@@ -0,0 +1,15 @@
+-module(tsuru_mdns_test).
+-author("erikh").
+
+-include_lib("eunit/include/eunit.hrl").
+
+publish_test() ->
+    {ok, NoBrokers} = tsuru_mdns:discover_brokers("_test._tcp.local", 1000),
+    ?assertEqual(NoBrokers, []),
+    tsuru_mdns:publish_broker("_test._tcp.local", "0.0.0.0", 8080),
+    Pid = tsuru_mdns:publish_broker("_test._tcp.local", "127.0.0.1", 8081),
+    tsuru_mdns:publish_broker("_other._tcp.local", "0.0.0.0", 8082),
+    {ok, TestBrokers} = tsuru_mdns:discover_brokers("_test._tcp.local"),
+    ?assert(proplists:is_defined("127.0.0.1", TestBrokers)),
+
+    ok.