diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -413,6 +413,7 @@
     'rsrc/js/application/projects/behavior-boards-dropdown.js' => '0ec56e1d',
     'rsrc/js/application/projects/behavior-project-boards.js' => 'c6b95cbd',
     'rsrc/js/application/projects/behavior-project-create.js' => '065227cc',
+    'rsrc/js/application/projects/behavior-reorder-columns.js' => '09eee344',
     'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf',
     'rsrc/js/application/releeph/releeph-request-state-change.js' => 'ab836011',
     'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'de2e896f',
@@ -645,6 +646,7 @@
     'javelin-behavior-releeph-request-typeahead' => 'de2e896f',
     'javelin-behavior-remarkup-preview' => 'f7379f45',
     'javelin-behavior-reorder-applications' => '76b9fc3e',
+    'javelin-behavior-reorder-columns' => '09eee344',
     'javelin-behavior-repository-crossreference' => 'f9539603',
     'javelin-behavior-search-reorder-queries' => 'e9581f08',
     'javelin-behavior-select-on-click' => '4e3e79a6',
@@ -872,6 +874,14 @@
       4 => 'javelin-util',
       5 => 'phabricator-busy',
     ),
+    '09eee344' =>
+    array(
+      0 => 'javelin-behavior',
+      1 => 'javelin-stratcom',
+      2 => 'javelin-workflow',
+      3 => 'javelin-dom',
+      4 => 'phabricator-draggable-list',
+    ),
     '0a3f3021' =>
     array(
       0 => 'javelin-behavior',
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1971,6 +1971,7 @@
     'PhabricatorProjectBoardController' => 'applications/project/controller/PhabricatorProjectBoardController.php',
     'PhabricatorProjectBoardDeleteController' => 'applications/project/controller/PhabricatorProjectBoardDeleteController.php',
     'PhabricatorProjectBoardEditController' => 'applications/project/controller/PhabricatorProjectBoardEditController.php',
+    'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php',
     'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php',
     'PhabricatorProjectColumn' => 'applications/project/storage/PhabricatorProjectColumn.php',
     'PhabricatorProjectColumnDetailController' => 'applications/project/controller/PhabricatorProjectColumnDetailController.php',
@@ -4842,6 +4843,7 @@
     'PhabricatorProjectBoardController' => 'PhabricatorProjectController',
     'PhabricatorProjectBoardDeleteController' => 'PhabricatorProjectBoardController',
     'PhabricatorProjectBoardEditController' => 'PhabricatorProjectBoardController',
+    'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController',
     'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController',
     'PhabricatorProjectColumn' =>
     array(
diff --git a/src/applications/project/application/PhabricatorApplicationProject.php b/src/applications/project/application/PhabricatorApplicationProject.php
--- a/src/applications/project/application/PhabricatorApplicationProject.php
+++ b/src/applications/project/application/PhabricatorApplicationProject.php
@@ -64,12 +64,16 @@
           '(?:query/(?P<queryKey>[^/]+)/)?' =>
           'PhabricatorProjectBoardViewController',
         'move/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectMoveController',
-        'board/(?P<projectID>[1-9]\d*)/edit/(?:(?P<id>\d+)/)?'
-          => 'PhabricatorProjectBoardEditController',
-        'board/(?P<projectID>[1-9]\d*)/delete/(?:(?P<id>\d+)/)?'
-          => 'PhabricatorProjectBoardDeleteController',
-        'board/(?P<projectID>[1-9]\d*)/column/(?:(?P<id>\d+)/)?'
-          => 'PhabricatorProjectColumnDetailController',
+        'board/(?P<projectID>[1-9]\d*)/' => array(
+          'edit/(?:(?P<id>\d+)/)?'
+            => 'PhabricatorProjectBoardEditController',
+          'delete/(?:(?P<id>\d+)/)?'
+            => 'PhabricatorProjectBoardDeleteController',
+          'column/(?:(?P<id>\d+)/)?'
+            => 'PhabricatorProjectColumnDetailController',
+          'reorder/'
+            => 'PhabricatorProjectBoardReorderController',
+        ),
         'update/(?P<id>[1-9]\d*)/(?P<action>[^/]+)/'
           => 'PhabricatorProjectUpdateController',
         'history/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectHistoryController',
diff --git a/src/applications/project/controller/PhabricatorProjectBoardReorderController.php b/src/applications/project/controller/PhabricatorProjectBoardReorderController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/controller/PhabricatorProjectBoardReorderController.php
@@ -0,0 +1,157 @@
+<?php
+
+final class PhabricatorProjectBoardReorderController
+  extends PhabricatorProjectBoardController {
+
+  private $projectID;
+
+  public function willProcessRequest(array $data) {
+    $this->projectID = $data['projectID'];
+  }
+
+  public function processRequest() {
+    $request = $this->getRequest();
+    $viewer = $request->getUser();
+
+    $project = id(new PhabricatorProjectQuery())
+      ->setViewer($viewer)
+      ->requireCapabilities(
+        array(
+          PhabricatorPolicyCapability::CAN_VIEW,
+          PhabricatorPolicyCapability::CAN_EDIT,
+        ))
+      ->withIDs(array($this->projectID))
+      ->executeOne();
+    if (!$project) {
+      return new Aphront404Response();
+    }
+
+    $this->setProject($project);
+
+
+    $project_id = $project->getID();
+
+    $board_uri = $this->getApplicationURI("board/{$project_id}/");
+    $reorder_uri = $this->getApplicationURI("board/{$project_id}/reorder/");
+
+    if ($request->isFormPost()) {
+      // User clicked "Done", make sure the page reloads to show the new
+      // column order.
+      return id(new AphrontRedirectResponse())->setURI($board_uri);
+    }
+
+    $columns = id(new PhabricatorProjectColumnQuery())
+      ->setViewer($viewer)
+      ->withProjectPHIDs(array($project->getPHID()))
+      ->execute();
+    $columns = msort($columns, 'getSequence');
+
+    $column_phid = $request->getStr('columnPHID');
+    if ($column_phid && $request->validateCSRF()) {
+
+      $columns = mpull($columns, null, 'getPHID');
+      if (empty($columns[$column_phid])) {
+        return new Aphront404Response();
+      }
+
+      // TODO: We could let you move the backlog column around if you really
+      // want, but for now we use sequence position 0 as magic.
+      $target_column = $columns[$column_phid];
+      $new_sequence = $request->getInt('sequence');
+      if ($target_column->isDefaultColumn() || $new_sequence < 1) {
+        return new Aphront404Response();
+      }
+
+      // TODO: For now, we're not recording any transactions here. We probably
+      // should, but this sort of edit is extremely trivial.
+
+      // Resequence the columns so that the moved column has the correct
+      // sequence number. Move columns after it up one place in the sequence.
+      $new_map = array();
+      foreach ($columns as $phid => $column) {
+        $value = $column->getSequence();
+        if ($column->getPHID() == $column_phid) {
+          $value = $new_sequence;
+        } else if ($column->getSequence() >= $new_sequence) {
+          $value = $value + 1;
+        }
+        $new_map[$phid] = $value;
+      }
+
+      // Sort the columns into their new ordering.
+      asort($new_map);
+
+      // Now, compact the ordering and adjust any columns that need changes.
+      $project->openTransaction();
+        $sequence = 0;
+        foreach ($new_map as $phid => $ignored) {
+          $new_value = $sequence++;
+          $cur_value = $columns[$phid]->getSequence();
+          if ($new_value != $cur_value) {
+            $columns[$phid]->setSequence($new_value)->save();
+          }
+        }
+      $project->saveTransaction();
+
+      return id(new AphrontAjaxResponse())->setContent(
+        array(
+          'sequenceMap' => mpull($columns, 'getSequence', 'getPHID'),
+        ));
+    }
+
+    $list_id = celerity_generate_unique_node_id();
+
+    $static_list = id(new PHUIObjectItemListView())
+      ->setUser($viewer)
+      ->setFlush(true)
+      ->setStackable(true);
+
+    $list = id(new PHUIObjectItemListView())
+      ->setUser($viewer)
+      ->setID($list_id)
+      ->setFlush(true)
+      ->setStackable(true);
+
+    foreach ($columns as $column) {
+      $item = id(new PHUIObjectItemView())
+        ->setHeader($column->getDisplayName());
+
+      if ($column->isHidden()) {
+        $item->setDisabled(true);
+      }
+
+      if ($column->isDefaultColumn()) {
+        $item->setDisabled(true);
+        $static_list->addItem($item);
+      } else {
+        $item->setGrippable(true);
+        $item->addSigil('board-column');
+        $item->setMetadata(
+          array(
+            'columnPHID' => $column->getPHID(),
+            'columnSequence' => $column->getSequence(),
+          ));
+
+        $list->addItem($item);
+      }
+
+    }
+
+    Javelin::initBehavior(
+      'reorder-columns',
+      array(
+        'listID' => $list_id,
+        'reorderURI' => $reorder_uri,
+      ));
+
+    return $this->newDialog()
+      ->setTitle(pht('Reorder Columns'))
+      ->setWidth(AphrontDialogView::WIDTH_FORM)
+      ->appendParagraph(pht('This column can not be moved:'))
+      ->appendChild($static_list)
+      ->appendParagraph(pht('Drag and drop these columns to reorder them:'))
+      ->appendChild($list)
+      ->addSubmitButton(pht('Done'));
+  }
+
+}
diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
--- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php
+++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
@@ -357,7 +357,16 @@
     $manage_items[] = id(new PhabricatorActionView())
       ->setIcon('fa-plus')
       ->setName(pht('Add Column'))
-      ->setHref($this->getApplicationURI('board/'.$this->id.'/edit/'));
+      ->setHref($this->getApplicationURI('board/'.$this->id.'/edit/'))
+      ->setDisabled(!$can_edit)
+      ->setWorkflow(!$can_edit);
+
+    $manage_items[] = id(new PhabricatorActionView())
+      ->setIcon('fa-exchange')
+      ->setName(pht('Reorder Columns'))
+      ->setHref($this->getApplicationURI('board/'.$this->id.'/reorder/'))
+      ->setDisabled(!$can_edit)
+      ->setWorkflow(true);
 
     if ($show_hidden) {
       $hidden_uri = $request->getRequestURI()
diff --git a/webroot/rsrc/js/application/projects/behavior-reorder-columns.js b/webroot/rsrc/js/application/projects/behavior-reorder-columns.js
new file mode 100644
--- /dev/null
+++ b/webroot/rsrc/js/application/projects/behavior-reorder-columns.js
@@ -0,0 +1,58 @@
+/**
+ * @provides javelin-behavior-reorder-columns
+ * @requires javelin-behavior
+ *           javelin-stratcom
+ *           javelin-workflow
+ *           javelin-dom
+ *           phabricator-draggable-list
+ */
+
+JX.behavior('reorder-columns', function(config) {
+
+  var root = JX.$(config.listID);
+
+  var list = new JX.DraggableList('board-column', root)
+    .setFindItemsHandler(function() {
+      return JX.DOM.scry(root, 'li', 'board-column');
+    });
+
+  list.listen('didDrop', function(node) {
+    var nodes = list.findItems();
+
+    var node_data = JX.Stratcom.getData(node);
+
+    // Find the column sequence of the previous node.
+    var sequence = null;
+    var data;
+    for (var ii = 0; ii < nodes.length; ii++) {
+      data = JX.Stratcom.getData(nodes[ii]);
+      if (data.columnPHID === node_data.columnPHID) {
+        break;
+      }
+      sequence = data.columnSequence;
+    }
+
+    list.lock();
+    JX.DOM.alterClass(node, 'drag-sending', true);
+
+    var parameters = {
+      columnPHID: node_data.columnPHID,
+      sequence: (sequence === null) ? 1 : (parseInt(sequence, 10) + 1)
+    };
+
+    new JX.Workflow(config.reorderURI, parameters)
+      .setHandler(function(r) {
+
+        // Adjust metadata for the new sequence numbers.
+        for (var ii = 0; ii < nodes.length; ii++) {
+          var data = JX.Stratcom.getData(nodes[ii]);
+          data.columnSequence = r.sequenceMap[data.columnPHID];
+        }
+
+        list.unlock();
+        JX.DOM.alterClass(node, 'drag-sending', false);
+      })
+      .start();
+  });
+
+});