diff --git a/zun_ui/api/client.py b/zun_ui/api/client.py
index b5c272e..baa0e1e 100644
--- a/zun_ui/api/client.py
+++ b/zun_ui/api/client.py
@@ -266,6 +266,12 @@ def availability_zone_list(request):
     return list
 
 
+def capsule_list(request, limit=None, marker=None, sort_key=None,
+                 sort_dir=None):
+    return zunclient(request).capsules.list(limit, marker, sort_key,
+                                            sort_dir)
+
+
 def image_list(request, limit=None, marker=None, sort_key=None,
                sort_dir=None, detail=False):
     # FIXME(shu-mutou): change "detail" param to True, if it enabled.
diff --git a/zun_ui/api/rest_api.py b/zun_ui/api/rest_api.py
index 3d4cd76..e2d05f4 100644
--- a/zun_ui/api/rest_api.py
+++ b/zun_ui/api/rest_api.py
@@ -163,6 +163,22 @@ class AvailabilityZones(generic.View):
         return {'items': [i.to_dict() for i in result]}
 
 
+@urls.register
+class Capsules(generic.View):
+    """API for Capsules"""
+    url_regex = r'zun/capsules/$'
+
+    @rest_utils.ajax()
+    def get(self, request):
+        """Get a list of the Capsules.
+
+        The returned result is an object with property 'items' and each
+        item under this is a Capsules.
+        """
+        result = client.capsule_list(request)
+        return {'items': [i.to_dict() for i in result]}
+
+
 @urls.register
 class Images(generic.View):
     """API for Zun Images"""
diff --git a/zun_ui/content/container/capsules/__init__.py b/zun_ui/content/container/capsules/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/zun_ui/content/container/capsules/panel.py b/zun_ui/content/container/capsules/panel.py
new file mode 100644
index 0000000..b8ae3ca
--- /dev/null
+++ b/zun_ui/content/container/capsules/panel.py
@@ -0,0 +1,19 @@
+#    Licensed 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.
+
+from django.utils.translation import ugettext_lazy as _
+import horizon
+
+
+class Capsules(horizon.Panel):
+    name = _("Capsules")
+    slug = "container.capsules"
diff --git a/zun_ui/content/container/capsules/urls.py b/zun_ui/content/container/capsules/urls.py
new file mode 100644
index 0000000..bb215ee
--- /dev/null
+++ b/zun_ui/content/container/capsules/urls.py
@@ -0,0 +1,20 @@
+#    Licensed 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.
+
+from django.conf.urls import url
+from django.utils.translation import ugettext_lazy as _
+from horizon.browsers import views
+
+title = _("Capsules")
+urlpatterns = [
+    url('', views.AngularIndexView.as_view(title=title), name='index'),
+]
diff --git a/zun_ui/enabled/_1332_project_container_capsules_panel.py b/zun_ui/enabled/_1332_project_container_capsules_panel.py
new file mode 100644
index 0000000..893ef56
--- /dev/null
+++ b/zun_ui/enabled/_1332_project_container_capsules_panel.py
@@ -0,0 +1,21 @@
+#    Licensed 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.
+
+# The slug of the panel to be added to HORIZON_CONFIG. Required.
+PANEL = 'container.capsules'
+# The slug of the panel group the PANEL is associated with.
+PANEL_GROUP = 'container'
+# The slug of the dashboard the PANEL associated with. Required.
+PANEL_DASHBOARD = 'project'
+
+# Python panel class of the PANEL to be added.
+ADD_PANEL = 'zun_ui.content.container.capsules.panel.Capsules'
diff --git a/zun_ui/static/dashboard/container/capsules/actions.module.js b/zun_ui/static/dashboard/container/capsules/actions.module.js
new file mode 100644
index 0000000..db5d5c9
--- /dev/null
+++ b/zun_ui/static/dashboard/container/capsules/actions.module.js
@@ -0,0 +1,58 @@
+/**
+ * Licensed 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.
+ */
+
+(function() {
+  'use strict';
+
+  /**
+   * @ngdoc overview
+   * @ngname horizon.dashboard.container.capsules.actions
+   *
+   * @description
+   * Provides all of the actions for capsules.
+   */
+  angular.module('horizon.dashboard.container.capsules.actions',
+    [
+      'horizon.framework',
+      'horizon.dashboard.container'
+    ])
+    .run(registerCapsuleActions);
+
+  registerCapsuleActions.$inject = [
+    'horizon.framework.conf.resource-type-registry.service',
+    'horizon.framework.util.i18n.gettext',
+    'horizon.dashboard.container.capsules.actions.refresh.service',
+    'horizon.dashboard.container.capsules.resourceType'
+  ];
+
+  function registerCapsuleActions(
+    registry,
+    gettext,
+    refreshCapsuleService,
+    resourceType
+  ) {
+    var capsulesResourceType = registry.getResourceType(resourceType);
+
+    // FIXME(shu-mutow): refresh action is dummy. remove it when add other action.
+    capsulesResourceType.itemActions
+      .append({
+        id: 'refreshCapsuleAction',
+        service: refreshCapsuleService,
+        template: {
+          text: gettext('Refresh')
+        }
+      });
+  }
+
+})();
diff --git a/zun_ui/static/dashboard/container/capsules/actions/refresh.service.js b/zun_ui/static/dashboard/container/capsules/actions/refresh.service.js
new file mode 100644
index 0000000..14c026a
--- /dev/null
+++ b/zun_ui/static/dashboard/container/capsules/actions/refresh.service.js
@@ -0,0 +1,68 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use self 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.
+ */
+
+(function() {
+  'use strict';
+
+  /**
+   * @ngDoc factory
+   * @name horizon.dashboard.container.capsules.refresh.service
+   * @Description
+   * refresh container.
+   */
+  angular
+    .module('horizon.dashboard.container.capsules.actions')
+    .factory('horizon.dashboard.container.capsules.actions.refresh.service', refreshService);
+
+  refreshService.$inject = [
+    'horizon.app.core.openstack-service-api.zun',
+    'horizon.dashboard.container.capsules.resourceType',
+    'horizon.framework.util.actions.action-result.service',
+    'horizon.framework.util.q.extensions'
+  ];
+
+  function refreshService(
+    zun, resourceType, actionResult, $qExtensions
+  ) {
+
+    var service = {
+      initAction: initAction,
+      allowed: allowed,
+      perform: perform
+    };
+
+    return service;
+
+    //////////////
+
+    // include this function in your service
+    // if you plan to emit events to the parent controller
+    function initAction() {
+    }
+
+    function allowed() {
+      return $qExtensions.booleanAsPromise(true);
+    }
+
+    function perform(selected) {
+      // refresh selected capsule
+      return $qExtensions.booleanAsPromise(true).then(success);
+
+      function success() {
+        var result = actionResult.getActionResult().updated(resourceType, selected.id);
+        return result.result;
+      }
+    }
+  }
+})();
diff --git a/zun_ui/static/dashboard/container/capsules/capsules.module.js b/zun_ui/static/dashboard/container/capsules/capsules.module.js
new file mode 100644
index 0000000..e2bb326
--- /dev/null
+++ b/zun_ui/static/dashboard/container/capsules/capsules.module.js
@@ -0,0 +1,150 @@
+/**
+ * Licensed 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.
+ */
+
+(function() {
+  'use strict';
+
+  /**
+   * @ngdoc overview
+   * @name horizon.dashboard.container.capsules
+   * @ngModule
+   * @description
+   * Provides all the services and widgets require to display the capsules
+   * panel
+   */
+  angular
+    .module('horizon.dashboard.container.capsules', [
+      'ngRoute',
+      'horizon.dashboard.container.capsules.actions'
+    ])
+    .constant('horizon.dashboard.container.capsules.events', events())
+    .constant('horizon.dashboard.container.capsules.resourceType', 'OS::Zun::Capsule')
+    .run(run)
+    .config(config);
+
+  /**
+   * @ngdoc constant
+   * @name horizon.dashboard.container.capsules.events
+   * @description A list of events used by Capsules
+   * @returns {Object} Event constants
+   */
+  function events() {
+    return {
+      CREATE_SUCCESS: 'horizon.dashboard.container.capsules.CREATE_SUCCESS',
+      DELETE_SUCCESS: 'horizon.dashboard.container.capsules.DELETE_SUCCESS'
+    };
+  }
+
+  run.$inject = [
+    'horizon.framework.conf.resource-type-registry.service',
+    'horizon.app.core.openstack-service-api.zun',
+    'horizon.dashboard.container.capsules.basePath',
+    'horizon.dashboard.container.capsules.resourceType',
+    'horizon.dashboard.container.capsules.service'
+  ];
+
+  function run(registry, zun, basePath, resourceType, capsuleService) {
+    registry.getResourceType(resourceType)
+    .setNames(gettext('Capsule'), gettext('Capsules'))
+    // for detail summary view on table row.
+    .setSummaryTemplateUrl(basePath + 'drawer.html')
+    // for table row items and detail summary view.
+    .setProperties(capsuleProperties())
+    .setListFunction(capsuleService.getCapsulesPromise)
+    .tableColumns
+    .append({
+      id: 'meta_name',
+      priority: 1,
+      sortDefault: true
+    })
+    .append({
+      id: 'id',
+      priority: 2
+    })
+    .append({
+      id: 'status',
+      priority: 1
+    })
+    .append({
+      id: 'cpu',
+      priority: 3
+    })
+    .append({
+      id: 'memory',
+      priority: 3
+    });
+    // for magic-search
+    registry.getResourceType(resourceType).filterFacets
+    .append({
+      'label': gettext('ID'),
+      'name': 'id',
+      'singleton': true
+    })
+    .append({
+      'label': gettext('Name'),
+      'name': 'meta_name',
+      'singleton': true
+    })
+    .append({
+      'label': gettext('Status'),
+      'name': 'status',
+      'singleton': true
+    });
+  }
+
+  function capsuleProperties() {
+    return {
+      'addresses': {label: gettext('Addresses'), filters: ['noValue', 'json'] },
+      'capsule_versionid': {label: gettext('Capsule Version ID'), filters: ['noValue'] },
+      'containers': {label: gettext('Containers'), filters: ['noValue', 'json'] },
+      'container_uuids': {label: gettext('Container UUIDs'), filters: ['noValue', 'json'] },
+      'cpu': {label: gettext('CPU'), filters: ['noValue'] },
+      'created_at': { label: gettext('Created'), filters: ['simpleDate'] },
+      'id': {label: gettext('ID'), filters: ['noValue'] },
+      'links': {label: gettext('Links'), filters: ['noValue', 'json'] },
+      'memory': { label: gettext('Memory'), filters: ['noValue'] },
+      'meta_labels': {label: gettext('Labels'), filters: ['noValue', 'json'] },
+      'meta_name': { label: gettext('Name'), filters: ['noName'] },
+      'project_id': { label: gettext('Project ID'), filters: ['noValue'] },
+      'restart_policy': { label: gettext('Restart Policy'), filters: ['noValue'] },
+      'status': { label: gettext('Status'), filters: ['noValue'] },
+      'status_reason': { label: gettext('Status Reason'), filters: ['noValue'] },
+      'updated_at': { label: gettext('Updated'), filters: ['simpleDate'] },
+      'user_id': { label: gettext('User ID'), filters: ['noValue'] },
+      'volumes_info': {label: gettext('Volumes Info'), filters: ['noValue', 'json'] }
+    };
+  }
+
+  config.$inject = [
+    '$provide',
+    '$windowProvider',
+    '$routeProvider'
+  ];
+
+  /**
+   * @name config
+   * @param {Object} $provide
+   * @param {Object} $windowProvider
+   * @param {Object} $routeProvider
+   * @description Routes used by this module.
+   * @returns {undefined} Returns nothing
+   */
+  function config($provide, $windowProvider, $routeProvider) {
+    var path = $windowProvider.$get().STATIC_URL + 'dashboard/container/capsules/';
+    $provide.constant('horizon.dashboard.container.capsules.basePath', path);
+    $routeProvider.when('/project/container/capsules', {
+      templateUrl: path + 'panel.html'
+    });
+  }
+})();
diff --git a/zun_ui/static/dashboard/container/capsules/capsules.service.js b/zun_ui/static/dashboard/container/capsules/capsules.service.js
new file mode 100644
index 0000000..9c18278
--- /dev/null
+++ b/zun_ui/static/dashboard/container/capsules/capsules.service.js
@@ -0,0 +1,61 @@
+/*
+ * Licensed 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.
+ */
+(function() {
+  "use strict";
+
+  angular
+    .module('horizon.dashboard.container.capsules')
+    .factory('horizon.dashboard.container.capsules.service', capsulesService);
+
+  capsulesService.$inject = [
+    'horizon.app.core.detailRoute',
+    'horizon.app.core.openstack-service-api.zun'
+  ];
+
+  /*
+   * @ngdoc factory
+   * @name horizon.dashboard.container.capsules.service
+   *
+   * @description
+   * This service provides functions that are used through
+   * the capsules of container features.
+   */
+  function capsulesService(detailRoute, zun) {
+    return {
+      getCapsulesPromise: getCapsulesPromise
+    };
+
+    /*
+     * @ngdoc function
+     * @name getCapsulesPromise
+     * @description
+     * Given filter/query parameters, returns a promise for the matching
+     * capsules.  This is used in displaying lists of capsules.
+     */
+    function getCapsulesPromise(params) {
+      return zun.getCapsules(params).then(modifyResponse);
+    }
+
+    function modifyResponse(response) {
+      return {data: {items: response.data.items.map(modifyItem)}};
+    }
+
+    function modifyItem(item) {
+      item.id = item.uuid;
+      item.trackBy = item.uuid;
+      item.trackBy = item.trackBy.concat(item.updated_at);
+      return item;
+    }
+  }
+})();
diff --git a/zun_ui/static/dashboard/container/capsules/drawer.html b/zun_ui/static/dashboard/container/capsules/drawer.html
new file mode 100644
index 0000000..4e0e9e9
--- /dev/null
+++ b/zun_ui/static/dashboard/container/capsules/drawer.html
@@ -0,0 +1,7 @@
+<hz-resource-property-list
+  resource-type-name="OS::Zun::Capsule"
+  item="item"
+  property-groups="[['id', 'cpu', 'memory', 'capsule_versionid'],
+                    ['meta_labels', 'created_at', 'updated_at'],
+                    ['addresses', 'volumes_info', 'status_reason']]">
+</hz-resource-property-list>
diff --git a/zun_ui/static/dashboard/container/capsules/panel.html b/zun_ui/static/dashboard/container/capsules/panel.html
new file mode 100644
index 0000000..c2c8950
--- /dev/null
+++ b/zun_ui/static/dashboard/container/capsules/panel.html
@@ -0,0 +1,4 @@
+<hz-resource-panel resource-type-name="OS::Zun::Capsule">
+  <hz-resource-table resource-type-name="OS::Zun::Capsule"
+                     track-by="trackBy"></hz-resource-table>
+</hz-resource-panel>
diff --git a/zun_ui/static/dashboard/container/container.module.js b/zun_ui/static/dashboard/container/container.module.js
index d6b1e30..cd341c2 100644
--- a/zun_ui/static/dashboard/container/container.module.js
+++ b/zun_ui/static/dashboard/container/container.module.js
@@ -24,6 +24,7 @@
   angular
     .module('horizon.dashboard.container', [
       'horizon.dashboard.container.containers',
+      'horizon.dashboard.container.capsules',
       'horizon.dashboard.container.images',
       'ngRoute'
     ])
diff --git a/zun_ui/static/dashboard/container/zun.service.js b/zun_ui/static/dashboard/container/zun.service.js
index a8084f0..6bbd8e5 100644
--- a/zun_ui/static/dashboard/container/zun.service.js
+++ b/zun_ui/static/dashboard/container/zun.service.js
@@ -27,6 +27,7 @@
   function ZunAPI(apiService, toastService, gettext) {
     var containersPath = '/api/zun/containers/';
     var zunAvailabilityZonesPath = '/api/zun/availability_zones/';
+    var capsulesPath = '/api/zun/capsules/';
     var imagesPath = '/api/zun/images/';
     var hostsPath = '/api/zun/hosts/';
     var service = {
@@ -52,6 +53,7 @@
       detachNetwork: detachNetwork,
       updatePortSecurityGroup: updatePortSecurityGroup,
       getZunAvailabilityZones: getZunAvailabilityZones,
+      getCapsules: getCapsules,
       pullImage: pullImage,
       getImages: getImages,
       deleteImage: deleteImage,
@@ -197,6 +199,15 @@
       return apiService.get(zunAvailabilityZonesPath).error(error(msg));
     }
 
+    //////////////
+    // Capsules //
+    //////////////
+
+    function getCapsules() {
+      var msg = gettext('Unable to retrieve the Capsules.');
+      return apiService.get(capsulesPath).error(error(msg));
+    }
+
     ////////////
     // Images //
     ////////////