Getting Started

What is Directus?

Directus is a free and open-source headless CMS and API for intuitively managing custom-schema database content.

As a developer, your projects require content that needs to be managed.

A Familiar Problem

As a developer, you're probably already managing your project's database content directly through Sequel-Pro, phpMyAdmin, or the command line. These are excellent tools because you get complete control with little setup beyond entering the database credentials. But you would never give that access to clients, designers, or even inexperienced engineers – relationships, validation, media, and security can easily get unwieldy. Not to mention you'll either have to build within one of those all-encompassing proprietary CMS designed for blogs, or build a custom CMS from the ground-up.

Custom Databases, No Workflow Assumptions

Directus allows developers to build projects with custom, scalable, and decoupled databases. No more proprietary templating systems and plugin piecemealing. Build with whichever technologies are most appropriate and then either connect directly to the database, via the Directus API, or use one of our SDKs. This makes it perfect for native-apps, web-apps, physical installations, and even projects that span multiple platforms.

Headless CMS

Once you have a basic database you'll likely want to start creating, editing, and managing its content. Directus gives even the most novice users the ability to safely and intuitively manage database content directly. Since it pulls all of its architecture and configuration directly from the schema, there's no need for time-consuming setup.

Comparison of data/content management options

Feature Directus DB Client Typical CMS
Manages existing (custom-schema) databases No
Works for non-website projects No
Allows custom SQL queries No
Granular table/field user permissions No No
Intuitive for non-developers No Some✝✝
Interface for relational data No
Interface for files/media No
Data API No
Free and open-source Some

High-level user permissions only
✝✝Often requires significant explanation or training

What about my website, app, or project?

Directus only manages the content in your database, beyond that you can use whatever technologies best fit your project. Not accustomed to so much freedom? Here are three easy ways to connect/access your data:

Or don't connect anything at all!

With the ability to create and manage any database schema you can dream up, maybe Directus is the project. Instead of paying $20/month for a rigid Project Management service, just throw together a quick schema with fields customized to your needs. Now you have a free solution that can easily be extended to accommodate any new info you want to track.

Example Schema: Project Management

├── projects
│   ├── id
│   ├── active
│   ├── title
│   ├── description
│   ├── url
│   ├── client
│   ├── date_started
│   ├── project_manager
│   └── sow_pdf
├── clients
│   ├── id
│   ├── logo_image
│   ├── name
│   ├── hourly_rate
│   ├── point_of_contact_email
│   ├── point_of_contact_name
│   └── point_of_contact_phone
├── tasks
│   ├── id
│   ├── active
│   ├── sort
│   ├── title
│   ├── category
│   ├── assigned_user
│   ├── created_by
│   ├── date_created
│   └── description
├── task_categories
│   ├── id
│   └── title
└── project_tasks (junction table)
    ├── id
    ├── project_id
    └── task_id



Directus is a forward-looking framework and therefore may not support certain legacy systems. If your server is not compatible with the requirements below, please contact your host to upgrade.

Optional Enhancement: Installing Imagick adds thumbnail support for TIFF, PSD, and PDF files

Server Preparation

  1. Check that your server meets the requirements
  2. Download and unzip the Directus package from here
  3. Create a database and MySQL user with access/modify privileges on your server
    • You can also use an existing database, but it's worth taking a look at the typical Directus Schema
  4. Upload the files to a public directory on your server
  5. Run the installation script by accessing the URL where you uploaded the files

Installation Walkthrough

Step 0 – Requirements

This pre-installation step will only be shown if the server requirements are not met.

Step 1 – Project Info

Step 2 – Database

Step 3 – Confirmation

Security Note: Once you have completed the install, make sure to delete install folder.


If you're having problems with your Directus install, please visit our troubleshooting section.


Now that you have Directus installed, you can customize it for your specific project. The first Directus user is always an administrator, which gives you access to the Directus Settings by clicking on the gear at the bottom of the sidebar.

Global Settings

Personalize this instance of Directus to fit your organization’s needs. Change colors, add a company logo, set the auto logout time, etc

File & Thumbnail Settings

Here you can adjust the size and quality of your automatic thumbnails, decide where to save uploaded files, and much more.

Changing these values will not effect any files already uploaded into Directus.

Tables & Inputs

Here you can create new tables and fields, or if managing an existing database, Directus provides intelligent default interfaces based on your datatypes. Either way, the real power comes from being able to make broad and granular adjustments to how Directus handles your inputs, interfaces, validation, and visualizations.

Table Options

Field Options

Clicking a table within Settings > Tables & Inputs lets you customize its individual fields. System fields such as id, active, and sort are displayed grayed out at the top.

Creating New Tables

  1. Navigate to the Settings > Tables & Inputs
    • Access Settings from the gear at the bottom of the sidebar
  2. Click the (+) button at the top of the page
  3. Type the name of the table and hit OK
  4. Click on the table within the listing to add fields

You can always add Tables directly to your database – then follow the steps below to allow that table to be managed by Directus.

Creating New Fields

  1. Navigate to the Settings > Tables & Inputs
    • Access Settings from the gear at the bottom of the sidebar
  2. Click on the table name you want to add the field to
  3. Click the "Add New Field: button at the bottom of the page
  4. Choose a field name and UI Type and click save
    • Optionally, you can update the Data Type or Length

You can always add new Fields directly to your database – then refresh Directus and they will apear.

Users, and Group Permissions

Chances are that additional users will need to access Directus. You can add as many users as you want within Directus, each assigned to a user-group with specific access/permissions.

Creating New User-Groups

  1. Navigate to the Settings > Group Permissions
    • Access Settings from the gear at the bottom of the sidebar
  2. Click the (+) button at the top of the page
  3. Type the name of the group and hit OK
  4. Click on the group within the listing to set its specific permissions.

Creating New Users

  1. Navigate to the Users page from the sidebar
  2. Click the (+) button at the top of the page
  3. Fill in the applicable fields. Required fields include:
    • First Name
    • Last Name
    • Email
    • Password
    • Group
  4. Save the user

Your Database

Choosing an Existing Schema

If you're looking to get up and running as quickly or possible, you may want to choose from our small but growing library of schema boilerplates. Directus will also work with most SQL schemas you find elsewhere – though the formatting of some table/field names may be less intuitive to users.

Creating & Customizing Schemas

Whether you're adapting a preexisting database or starting from scratch – the following guidelines will help you get the most out of Directus. Below is an example SQL query for generating a simple table that considers these guidelines:

CREATE TABLE `articles` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `active` tinyint(1) unsigned DEFAULT '2',
  `sort` int(11) DEFAULT NULL,
  `title` varchar(100) DEFAULT NULL,
  `article` text,
  `publish_date` datetime DEFAULT NULL,
  `author` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)

Table & Field Naming

Directus auto-formats all table and field names for presentation to the users, so it's important to consider your naming system. Beyond that – you should make sure that your field names would make sense when formatted and read by users.


Not so great:

Primary Key

Currently Directus requires that every table contains an auto-incremented primary key named id.

`id` int(11) unsigned NOT NULL AUTO_INCREMENT

The abstracting/decoupling of this field name is under development.

Status Field

If you would like the items of a table to track a "status" state, you can easily do that within Directus. A status could be used in many different ways, but the most common is: Published, Draft, or Deleted, but you could extend or customize this as needed.

It is important when setting up an app to honor any Status states used by Directus. Typically this means only showing active/published content. Assuming you are using the default Status options, that would mean limiting all SQL queries to:

AND `active` = '1'

Learn More

Sort Field

Adding a sort(INT11) field to a table turns on Directus' drag-and-drop sorting. Items on the Directus listing page will now have handles for dragging them into curated orders. When sorted, Directus will save ascending integers into the order field, thereby making it easy to return results in this order:


If a table will likely have many items (), it may be advisable not to use the manual sort feature. Dragging items within long lists can be difficult – and the sort feature disables when items span multiple pages.


Directus uses the datatype of the field to determine which Inputs/Interfaces are allowed. The most common one is chosen as a default, but you can always change this in the Settings > Tables & Inputs. Below are a few examples of how a datatype may be used:


Directus uses your database defaults for all inputs/interfaces. So if you give that TINYINT a default value of 1, the checkbox input will be checked by default.


You can add field comments directly in your database, those will auto matically get pulled in to the user as a note on that input. This is especailly useful to help clarify field names and purpose for the users of Directus.


If you're looking to create relationships in your database this is done through relational ids (one-to-many) and junction-tables (many-to-many). Since Directus requires an id unique primary key for each table, this is what is saved in the INT(11) relational field you setup.

One-to-Many & Many-to-One

The easiest relationships, these are created within Directus or the database itself. In the example below, we're

  1. Create the field to store the related table's id
  2. Setup the relationship within Directus or using the following query:
INSERT INTO `directus_columns`
  (`table_name`, `column_name`, `data_type`, `ui`, `relationship_type`, `table_related`, `junction_key_right`)
  ('articles', 'author', 'INT', 'many_to_one', 'MANYTOONE', 'directus_users', 'user');


Again, these can be created within Directus or the database itself. In the example below, to create a slideshow interface we're adding in a relationship between the articles table we just created and the directus_files table.

Create the junction table – remember to add the id column and two fields for holding the relational id.

Optional Note: You can add a sort column (see above) to allow the relational items to be manually sorted with drag-and-drop.

CREATE TABLE `article_images` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `sort` int(11) DEFAULT NULL,
  `article_id` int(11) DEFAULT NULL,
  `file_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)

Setup the relationship – can be done within Directus or using the following query:

INSERT INTO `directus_columns`
  (`table_name`, `column_name`, `data_type`, `ui`, `relationship_type`, `table_related`, `junction_table`, `junction_key_left`, `junction_key_right`)
  ('articles', 'slideshow', 'MANYTOMANY', 'multiple_files', 'MANYTOMANY', 'directus_files', 'article_images', 'article_id', 'file_id')

Field Order

By default Directus will display table inputs in the same order as the database fields, however this can be overridden within Directus Settings>Tables & Inputs. The reason we don't alter the fields within the database itself when changing input order is because some database optimization involves the specific ordering of the fields.


Step by Step

Below is the process for manually updating Directus. Directus Hosted Instances are automatically updated.

  1. Backup your database: In addition to scheduled backups, you should always create another complete database backup directly before making any broad CMS/database changes
  2. Download and unzip the newest Directus package from here
  3. Ensuring you don't replace your /api/config.php and /api/configuration.php files, overwrite your previous version of Directus with the new files
  4. Run the migration script to update your database/schema. Read how to

Developers who installed Directus by cloning the git repository directly to their server can simply pull the newest stable version. Typically this is as simple as navigating to the Directus folder and running: git pull origin build

User Guide


If you attempt to access Directus without being authenticated, you will be directed to the login page. Once successfully logged in, if you were initially following a link to a certain page you will be redirected there – otherwise you'll be taken to the last page you visited within Directus.

Developer Note: That the Directus version is displayed in the bottom-left corner of the login screen. Hovering over the version will reveal the exact git commit hash of your version.

Forgot Password

Directus always salts and securely encrypts your password, so no one, not even admins, can see your actual password in plain-text. Therefore no one can tell you what your password is – if forgotten, it must be reset. To reset your password, navigate to the login page, fill in your email address, and click the "?" within the password input. You will receive an email with a link to reset your password that is valid for a short period of time.

Logging Out

It's a good habit to log out of Directus when not actively using it – especially on public computers/devices. The logout button is always available at the bottom of the sidebar, beside your user avatar. Administrators of your system may choose to set an Auto Logout Duration for idle users – by default this is set to 1 hour.

Security Note: Leaving your computer awake and unattended while your Directus user is logged-in could result in a security breach.


This straightforward page gives a listing of all tables your user has access to see. For ease of access, tables are listed directly in the Directus sidebar, so you will rarely need to visit this page. To manage a new database table within Directus, an admin must first add it to Directus and a user-group's privileges. If you think a table is missing, contact one of your Instance administrators.

Item Listing

This is one of the more useful and data-rich pages within Directus. It provides a tabular listing of all the items (records) within the selected table. Clicking the (+) at the top of the page will create a new item in this table, or click on any of the listed items to view/edit it. Beyond this basic functionality, there are also these other features covered below:


The “Add Filter” dropdown will add inputs for searching/filtering within specific fields. Each filter you add is considered an “AND” (not "OR") – for example, with the filters title="Happy" and location="New York" items would have to match (contain) both. There are several input types for filters: date-chooser, checkbox, dropdown, auto-complete, and the default text-box. Clicking the “×” inside the input will remove that filter.

Active, Inactive, & Delete

For tables that contain a Status column (Directus uses active by default), when selecting items from the listing page you will have the option to change their active status:

Additionally, there is a dropdown in the header for choosing which item's are visible based on status. The options here are:

Developer Note: Remember to respect the above statuses by only fetching active items (active=1) in your product's data queries. Updating the config file to change the default Status field name, allowed values, and colors.

Changing Visible Columns

By default the item listing page shows the first few columns of data for any given table. Users can then adjust which columns they see by hovering over the gray table header and clicking the gear on the right – in the dropdown that opens, choose up to eight (8) fields to show. Due to their complexity, many-to-many fields such as the Slideshow UI can not be shown.


As is the case with most tabular data, clicking column headers will sort the data by that field. Clicking a column again will reverse the sort direction – clicking a third time will remove sorting reverting to the default: date created. This is automatically saved on a per user basis and is persistent until changed.


While sorting simply changes the current view of the items, reordering actually saves the order within each item. For tables that contain a “sort” column, items will automatically show a handle on the left side of their row and become re-orderable through drag-and-drop.

Note: While reordering is an excellent way to curate the order of items, you can not drag-and-drop between pages. One work-around is to increase the Items Per Page variable – but it is not advisable to reorder very large datasets in this way.


After setting up all of the above options (Filters, View Status, Visible Columns, Sorting) you may want to save it for later use. To save your current layout: simply click the "Add Bookmark" link within the sidebar and give it a name – it will then appear in the Bookmarks section (for your user). To delete a Bookmark, click the "×" beside it's name in the sidebar.

Bulk Edit

When selecting multiple item checkboxes, a toolbar option for “Batch Editing” will appear. Clicking into this mode will take you to a blank edit page where edits can be made. On this page, clicking “edit” for a particular field indicates that Directus should save that value for all selected items.

Caution: Use this feature with care as it is easy to overwrite a large amount of items.


For certain columnar data (eg: Integers), a footer will show at the bottom of the table with additional calculated information:

Create & Edit

These pages display all the fields the current user has permission to view. Admins can change the field order, interface type, and field notes within Settings > Tables & Inputs. If your project would benefit from proprietary or otherwise custom data interfaces, it's easy to extend our Core interfaces or create new ones – there are no limits to what you can create!


Once you've added or edited data on the page, you'll probably want to save those changes. As with other Directus pages, the (✓) button at the top of the screen will both save and return you to the listing. On this page however, to the right of the primary button there's an additional triangle dropdown providing three additional options:

Item Comments

In addition to a full messaging system, you can also leave comments on individual items from their detail page. For example, you could let the other users know that the item still needs to be reviewed before going live – or reference specific users with the "@" character. When referenced in comments, users will receive a Directus notification and, if set within their preferences, an accompanying email.

Revision History

Every change made by users within Directus is tracked to give comprehensive accountability. The user, their IP address and user-agent, the date/time of the change, any effected data (delta), and a complete item backup are all saved for each and every edit. You can see a history of these changes at the bottom of this item detail page – commingled chronologically with any item comments.


This page provides a listing of all files uploaded through the Directus system, it also includes any videos or embeds (eg: YouTube, Vimeo) that have been linked.


IPTC – (location, caption, keywords) is extracted from the files when they're uploaded and automatically stored with each file.

Custom Fields

You can add as many additional custom fields to directus_files as you'd like.


There are two types of messages within Directus:

Item Comments

Every item within the system can contain comments attached within the Item History for associating specific feedback.

User Messages

Similar to email, users can send messages to other Directus users. An option exists to have all Messages forwarded to your email address.

Users & Groups

You need to authenticate (log in) as a user to access data through Directus or its API. Each user is assigned to a user-group which defines it's access privileges with Directus. When you install Directus your first user is in the admin group – the highest level possible – with unrestricted data access, the ability to create and edit users, and access to the Settings area.

Each user is organized by group on the listing page and has the following default fields for storing information:

Note: Adding additional custom fields to the users table is a great way to quickly create a company directory.

IP Whitelist

If turned on for a user-group, those users will only be able to connect to Directus when accessing over specific IP addresses. This is useful for ensuring a group of users can’t access the system from personal, public, or insecure networks/computers.

What is Directus Hosted?

Directus Hosted is a highly scalable DBaaS (Database as a Service) platform for developers looking to easily give their clients an intuitive cloud-based interface for managing app/website content.

With a Directus Hosted account you can manage all your instances in one place, creating new Directus instances in seconds. Simply choose a name and size for your instance and within seconds you'll have a new SQL database ready to completely customize with your project's specific data architecture. Use the dynamic RESTful API to pull data into your app or website – or just use Directus as a standalone tool, similar to FileMaker. Either way, an unlimited number of users can upload files and manage database content, all based on granular group permissions.

Your Account Dashboard

From here you can manage your account info, including your email, password, and payment details. From this panel you can also review your account's billing history, see any outstanding balance you might have, and close your account.

Creating Instances

Once you have added payment info into your account you can easily and quickly add new instances. Just click on the "Create Instance" button in the header, fill in an instance name, and choose a plan – that's it. Names should be lowercase alphanumeric with dashes instead of spaces – choose your instance name carefully as that will determine your database name and API URL. It should only take a few second for your database and Directus CMS to be set up. Then, once you've created a schema and content, your dynamic API will be available.

Managing Instances

Once you've created a few instances, you can easily manage them all in one place from the Instances section. In addition to changing your instance's plan (resizing), you will also be presented with other details, including:

You will also be able to see all of your API users/keys listed here. Each Directus user has an associated API key that inherits its data privileges. Keys can be be updated within the instance's Directus Users section.

Deleting Instances

You can delete instances from their detail page. There are three levels of confirmation to ensure you do not accidentally destroy instances. The following is permanently destroyed when deleting an instance:


Core Interfaces

User Interfaces (UIs) are how your users will interact with the different types of content in your database. Each field in your database has a data-type that determines what kind of content can be stored there – Directus uses this information to determine a default UI and any other UIs that are supported. In addition to the provided Core UIs, you can also easy create new Custom UIs for specific or complex use-cases.

Text Input

Supported Datatypes: VARCHAR, DATE, TIME, ENUM

A simple, basic, single-line text field for almost any kind of string data. The user is shown the remaining character count based on the length property of the column. Despite being so simple, a few key options make this UI one of the most useful and flexible:

Text Area

Supported Datatypes: TEXT, VARCHAR

A multi-line text field for longer plain-text data. New lines are saved in the database as new lines as this input does not create any HTML tags.


Supported Datatypes: VARCHAR, TEXT

A multi-line text field for longer HTML content with WYSIWYG formatting buttons. Each formatting button can be toggled on or off as needed.

Slug, or Short Name

Supported Datatypes: VARCHAR

Often clean/semantic URLs will include a contextual identifier based on an item's title rather than the id alone. This UI automatically creates that URL-friendly value based on another input. For instance, if linked to the title column, as the user types "This, That & the Other!" the slug input would live-update to "this-that-the-other".

Password & Salt

Supported Datatypes: VARCHAR

This UI contains a masked input for entering a password, a second input to confirm the password, a "Generate New" button for optionally generating new secure passwords quickly, and a "Reveal Password" toggle to expose the password to the user in plain-text. This UI can also be linked to a "salt" column so that the password entered is securely hashed.



TODO: Combine with text-field. A simple input that only accepts number characters. Alternatively you could use the text-input UI and set the appropriate validation.

Hex Color

Supported Datatypes: VARCHAR

Accepts only 6 hexadecimal characters and provides a live-colored square matching the value. This UI also uses the HTML5 color-chooser for easy color selection. Allowed hexadecimal characters include: 0123456789ABCDEF

Datetime (Date, Time, and Year)

Supported Datatypes: DATETIME, DATE, TIME, YEAR This UI provides a set of drop-downs and numerical inputs for entering months days, years, hours, minutes and seconds based on the datatype. HTML5 helpers are available for easier date/time selection. All values are saved in the ISO 8601 standard: yyyy-mm-dd hh-mm-ss

Select (Drop-down or Radio Buttons)

Supported Datatypes: VARCHAR, INT

Sometimes you don't need to create a dedicated table to house relational dropdown options but you still want to limit the values a user can choose. This UI will save a single non-relational value chosen from a set of JSON options.

TODO: Merge in Radio Buttons

Multi-Select (List or Checkboxes)

Supported Datatypes: VARCHAR, TEXT

Sometimes you don't need to create a dedicated table to house relational dropdown options but you still want to limit the values a user can choose. This UI will save multiple non-relational values from a set of JSON options. All selections are glued together with a delimiting character.


Supported Datatypes: TEXT, VARCHAR, CHAR

A system for entering and saving comma-delimited tags – for relational tags use the many-to-many UI instead. Tags are entered into the list upon hitting the comma or enter key and are deleted by clicking the tag in the list. The tags are saved into the database as a CSV string with commas at the beginning and end (",ranger,studio,range,") which lets you perform powerful LIKE filters. For example, by using commas you can filter with %,range,% to get results that are exact (does not include "ranger") or %range% for inclusive (does include "ranger").


Supported Datatypes: TINYINT

Checkbox is the default UI for the TINYINT datatype – saving either a 1 (checked) or 0 (unchecked). It also honors the default value of the database, which is the proper way to set the UI's initial state.


Supported Datatypes: INT

An HTML5 adjustable slider for choosing a number.


Supported Datatypes: VARCHAR, TEXT

TODO: Add ALIAS datatype support and remove VARCHAR and TEXT. No need to have an actual column for this.

This is an easy way to include verbose or formatted instructions or other helper information within the Item Detail page. If the column Note isn't enough room to adequately describe the column's guidelines or you need table/level instructions, this is a good way to add context.


Supported Datatypes: VARCHAR, ALIAS

This UI provides an visual and interactive way to select a specific location, which is then Geocoded and dynamically fills in any linked address fields.

Relational Data

Since SQL is a relational database, Directus has several UIs that can help clearly and easily manage relationships between items. An item can have related items from the same table (eg: Project -> Related Project) or a different table (eg: Project -> Category). An item can also have relationships to a single item (eg: Shirt -> Size) in a Many-to-One (or One-to-Many) relationship, or to multiple items (eg Shirt -> Materials) through Many-to-Many relationships.

Many-to-One (M2O)

Many-to-One relationships save the id from another item as it's value, creating a dynamic relationship to that item.

Many-to-Many (M2M)

Many-to-Many relationships are quite a bit more complex, but very powerful. There are two things that are important to understand for M2Ms: Junction Tables, and Aliases. Let's use our Shirt -> Materials example from before to explore how this works:

shirt (Table)
* id
* name
* size
* "materials" (M2M Alias)

materials (Table)
* id
* material

shirt_materials (Junction Table)
* id
* shirt_id
* material_id

Junction Tables

We can't efficiently store multiple values in a single field, so M2M fields store all their relationships in a separate table called a "junction table" created just for this purpose. Junction tables are very simple – like all Directus tables they have an id column, but they also have two more columns for storing the IDs of the two items it's relating. In this case we're linking a shirts with their respective materials – so we have a shirt_materials junction table with shirt_id and material_id columns.


Another thing you may have noticed is that the materials column says Alias and has quotes around it. Since we're not actually storing anything in that column (remember, values are stored in the Junction Table) we don't need to create it in the database. Instead, Directus uses an Alias column (like a ghost column) to represent a column for M2M UIs. Aliases columns don't exist in your database schema, they are added directly into the directus_columns table (a good place to look for bulk creation or troubleshooting).

Since relational UIs can get slightly complex, if you can't get one working or run into issues check out our Troubleshooting Relationships section.

Single File (M2O)

Supported Datatypes: INT

This is a M2O relational UI that links a single file (and YouTube/Vimeo video embeds) by storing the related item's ID – which is why this UI only works with the INT datatype (also used for IDs). If you have a books table you could use this for the cover_image column, for a projects table this would be the UI used for columns like: thumbnail_image, wireframe_pdf, and background_video. Basically any column that needs to store a single file.

Multiple Files (M2M)

Supported Datatypes: MANYTOMANY (an alias datatype)

This is a M2M relational UI that links multiple files (and YouTube/Vimeo video embeds). If you have an album table you could use this to store mp3s within tracks, for a projects table this would be the UI used for columns like: hero_slideshow, youtube_playlist, and press_pdfs. Basically any column that needs to store multiple files. Currently only Directus Files are accepted by this UI, so directus_columns.table_related must always be set to directus_files.

Many-to-Many (M2M)

Supported Datatypes: MANYTOMANY (an alias datatype)

This is a M2M relational UI that will make it easy to link the item being edited to multiple other items from another table (or the same table). You can use this to relate multiple tags to projects, or multiple staff to their respective office. But be careful about creating a schema/architecture with recursive or deeply nested M2Ms – otherwise you'll end up with a confusing "Matryoshka doll" user experience.

Many-to-One (M2O)

Supported Datatypes: INT

This is a M2O relational UI drop-down that links to a single item by storing the related item's ID in the column – which is why this UI only works with the INT datatype (also used for IDs). You could use this to relate each item in your press table to a project, or to relate

Many-to-One Type-Ahead

Supported Datatypes: INT

Similar to the Many-to-One in function, the interface for this UI is a type-ahead (live search auto-complete) which is useful for large relational datasets that won't fit into a dropdown.

One-to-Many (O2M)

Supported Datatypes: ONETOMANY (an alias datatype)

Similar to Many-to-One, this UI is an interface for the opposite direction. Instead of saving the id of a related item in an actual column, the One-to-Many is an Alias column that saves a FK on the related items.

When deleting these related O2M items, the following rules apply to the related table:

Translation (O2M)

Supported Datatypes: ONETOMANY (an alias datatype)

This relational UI allows for field translations to be stored in a related table. For example, if you have a projects table you could create a projects_translations table to relationally store the name and description columns in other languages. Some data, such as hero_image or featured may not need to be translated and can be added directly to the projects table.

Example Schema

UI Options

Relational Options

When creating the translation column within Directus you'll need to enter some info about the relationship. These are some helpful reminders:

Custom User Interfaces (UIs)

UI Structure

For most content types the Directus Core User-Interfaces should work well. However if you're looking for a specific interface or have proprietary or custom content types to manage – you may need or want to create something more tailored. Directus UIs are modular Javascript files (or dedicated sub-directories for complex interfaces) that allow you to create new ways of interfacing with database content.

Text inputs, checkboxes, and WYSIWYG editors are pretty standard – but perhaps your application needs a JSON editor, unique hashtag generator, iTunes song lookup, or custom seating-chart. So let's look at how these would be created within Directus, starting with the components of the UI.


Each UI must have a unique identifier. For instance the Single File ID is single_file.


Each UI works with a specific set of datatypes, for instance a Text Area UI might work with TEXT or VARCHAR datatypes, while a Single File UI only works with INT (storing the related file ID).


Options are the key to making the UI usable in different circumstances. Options are easy to create, and actually use OTHER UIs as the interface for setting the value (so meta). Below is an example of an dropdown option to determine the size of an interface:

{id: 'size', ui: 'select', required: true, options: {options: {'large':'Large','medium':'Medium','small':'Small'} }, comment: "Choose a size!"},


Whether it's simple enough to add (escaped) in the single Javascript file, or to be included from a separate template file – this is where you define your markup and any additional styling. Directus uses the Handlebars templating library – so be sure to reference their Docs for any questions you may have.


In addition to how the content is manipulated on the Edit Item page, you also need to consider how the data will be presented on the Item Listing page (more tabular). Each UI has a separate list function to handle that view.


Of course you're going to want to ensure that data is saved correctly into the column – so you should always include logic in the validate function.

Creating Your First UI

The easiest way to get started is to take one of the existing Core UIs and duplicate it. If possible, try to find one with similar functionality that supports the same datatype(s). Here are the steps for using an existing UI as a starter template to create your new UI:

  1. Copy a Core UI file from /app/core/uis into the custom UI directory: /customs/uis, giving it a new name.
  2. Set a unique name for the UI:
  3. Choose which SQL datatypes this UI will support: Module.dataTypes
  4. Optionally, add UI Options that admin users can adjust per column: Module.variables
  5. Draft up some markup with inline styling within the template variable.
    • You will also see examples of escaped CSS in the template variable, but remember to namespace any styling.
  6. Add any validation into the Module.validate function which is called when attempting to save the model. If there is an error message the system automatically shows it as an alert. There are two parameters available within this function:
    • value: String : Submitted value for this column's UI
    • options: Object : Contains the options from Module.variables for this UI
      • collection [TableCollection]
      • model [EntriesModel]
      • schema
      • settings
  7. Finally, format the value for display on the Item Listing page within the Module.list function. There is one parameter available:
    • options: Object : Contains the value and options for this UI
      • value
      • collection [TableCollection]
      • model [EntriesModel]
      • schema
      • settings

At this point, all you have to do is refresh your instance of Directus and your new UI will be available. If you go to Settings > Tables & Inputs > []Table] and open the User Interface dropdown for the desired column, if that column's datatype is supported you will see your UI as an option. If you don't see it, confirm that that datatype is listed in Module.dataTypes and that the is unique – otherwise check the error logs.

Example UI

As an example we are going to create a Text Area with a limited character-count, and will update the remaining characters as the user add or remove characters from the interface.

UIs depends on two objects, UIComponent and CustomUIView (optionally).


define(['core/UIComponent', 'core/CustomUIView'], function(UIComponent, UIView) {
    var charactersLimit = 100;
    var Input = UIView.extend({
        templateSource: '<textarea maxLength="{{maxLength}}"></textarea><span id="count">{{charactersRemaining}}</span>',
        events: {
            'keyup textarea': 'updateCount'
        updateCount: function(event) {
            var textLength = event.currentTarget.value.length;
            var textRemaining = charactersLimit - textLength;
        serialize: function() {
            return {
                maxLength: charactersLimit,
                charactersRemaining: charactersLimit

Template Source

Template source can be any valid Handlebars source, which include plain HTML and CSS. For larger or more complex UIs you may wish to include a separate template file instead of escaping the markup in Javascript.


We can bind events to any element inside the UIView but we cannot bind to elements outside the view. If for some reason you want to bind to a element outside, you can use the global jquery object $.

Listening to keyup events on the text area will execute updateCount method.

Update Count

The uppdateCount method will execute every time the user enter characters in the text area and updates the counter.


The serialize method returns the data that is going used when templateSource is rendered.


var Component = UIComponent.extend({
    id: 'textlimit',
    dataTypes: ['TEXT'],
    Input: Input


id is the UI's unique name.

Data Types

dataTypes are the supported data types for the UI. In this case we want to support only TEXT column type. The first value here is the default.


This is the input view for the component.

Complete UI code

define(['core/UIComponent', 'core/CustomUIView'], function(UIComponent, UIView) {
    var charactersLimit = 100;
    var Input = UIView.extend({
        templateSource: '<textarea maxLength="{{maxLength}}"></textarea><span id="count">{{charactersRemaining}}</span>',
        events: {
            'keyup textarea': 'updateCount'
        updateCount: function(event) {
            var textLength = event.currentTarget.value.length;
            var textRemaining = charactersLimit - textLength;
        serialize: function() {
            return {
                maxLength: charactersLimit,
                charactersRemaining: charactersLimit

    var Component = UIComponent.extend({
        id: 'textlimit',
        dataTypes: ['TEXT'],
        Input: Input

    return Component;

Enabling an UI

To use a UI simply copy it into your /customs/uis directory.

UI Options

In the previous section we explained how to create a UI that limits how many characters they can type into a text area while showing the characters remaining.

In the example, we directly create a variable with a 100 character limit – but what if this limit is changed? What if you want to use different limit on a different field? The solution to this is UI options.

UI options are settings for each interface found within the Setting panel. These values are not shared between UIs, but are specific to each column.

Adding options

Add a new property inside Component object named variables.

variables property is an array of objects that will each represent a setting inside the column settings editor.

Options structure

The structure of each option is the following:

    id: 'option-id',
    ui: 'textinput',
    default_value: 'default_value',
    comment: 'This is a comment for this ui setting'

Now let's add a new option for our Text Area characters limitation UI:

var Component = UIComponent.extend({
        id: 'textlimit',
        dataTypes: ['TEXT'],
        variables: [
            {id: 'characters_limit', ui: 'numeric', default_value: 100, comment: 'Characters Limitation'}
        Input: Input


define(['core/UIComponent', 'core/CustomUIView'], function(UIComponent, UIView) {
    var Input = UIView.extend({
        templateSource: '<textarea maxLength="{{maxLength}}"></textarea><span id="count">{{charactersRemaining}}</span>',
        events: {
            'keyup textarea': 'updateCount'
        updateCount: function(event) {
            var charactersLimit = this.options.settings.get('characters_limit');
            var textLength = event.currentTarget.value.length;
            var textRemaining = charactersLimit - textLength;
        serialize: function() {
            var charactersLimit = this.options.settings.get('characters_limit');
            return {
                maxLength: charactersLimit,
                charactersRemaining: charactersLimit

    var Component = UIComponent.extend({
        id: 'textlimit',
        dataTypes: ['TEXT'],
        variables: [
            {id: 'characters_limit', ui: 'numeric', default_value: 100, comment: 'Characters Limitation'}
        Input: Input

    return Component;

Other Inputs

System UIs

These are Core UIs that are used exclusively by the Directus framework for internal purposes.

Other UIs to Document



Composer dependency management

The Directus API is written in PHP and employs a number of third-party, open-source packages to manage routing, rendering, database access, and so on. See the core Composer documentation here.

Best practice says that the composer.lock file should be tracked in the application repository. This helps to ensure stability by guaranteeing that over time, and across the development team, the versions of application dependencies will not change with each installation, but will remain exactly the same.

This is especially critical from the perspective of Directus code integration and inter-dependency integration. Case in point: the Slim package (version 2.4) introduced breaking changes in its integration with Twig. Therefore changes to the composer.lock file are extremely sensitive. Here are some guidelines on how to modify Directus’ composer dependencies.

Never run composer.phar update

This “updates” the version of each dependency to the latest version. If for whatever reason you need to re-install your vendors, always instead remove your vendor directory and re-run composer.phar install

Avoid modifying the composer.json file directly

Always modify dependencies using composer.phar because it will modify the lock file directly. For example, to add a new dependency, run this command:

composer.phar [require]( vendor/package:version

This command updates your JSON file and your lockfile without changing the versions of your other dependencies.


Step 1: Setup a Development Environment

You will need to setup a local *AMP (Apache, MySQL and PHP) or *EMP (nginx, MySQL and PHP) environment in order to test Directus. *AMP packages can be found for different platforms:

Step 2: Install git

You will need Git to keep your test version of Directus up to date. Unless you already have Git installed, you can download it from here.

Step 3: Pull down Directus from GitHub

Create a directory named "directus" in the www root. Next, open the directory in the terminal and type:

$ git clone directus

Step 4: Install Dependencies

Directus uses composer to handle its php dependencies. Go to the directus/ folder and install composer:

$ curl -s | php

Then install the dependencies by executing:

$ php composer.phar install

Read more at the official site on how to download and install composer, or how to install composer globally.

Step 5: Setup the Database

The AMP-packages listed above all include Phpmyadmin. The following three steps need to be completed in order to setup the database

Please disable MySQL Strict Mode before installation. Directus does not fully support Strict Mode due to limitations with the PDO and MySQL Drivers.

Using PHPMyAdmin

  1. Create the database
    1. Databases -> Create Database
    2. Type directus as name and select utf8_general_ci as Collation.
  2. Create a user
    1. Once you've created the database it will be visible in the left column – open it by clicking it.
    2. Privileges -> Add user
    3. Fill in username and password. Leave the other fields as defaults.
  3. Import the Directus core database schema
    1. Import->File to Import->Choose file
    2. Open api/schema.sql
    3. Press Go

Using Command Line

  1. Connect to MySQL Server

    $ mysql -u <mysql-user-name> -p
    # mysql will ask to type the server password.
    $ Enter password: ****

    Change with the database username, typically is root by default.

  2. Create the Database After successfuly connect to the MySQL Server, create a database by typing

    mysql> CREATE DATABASE <database-name>`;

    Change with the desired name for the database.

    And you can exit the server by typing exit.

  3. Install Directus
    1. Go to http://your-directus-host.local/installation and follow the steps and skip Step 6
    2. Or Import Directus core database schema and do Step 6
      $ mysql -u <mysql-user-name> -p <database-name> < api/schema.sql
      # mysql will ask to type the server password.
      $ Enter password: ****

Step 6: Setup Directus

Open api/config_sample.php Add the database username and password from Step 5 to DB_NAME, DB_USER and DB_PASSWORD. Save the file as directus/api/config.php

define('DB_USER',       'myusername');
define('DB_PASSWORD',   'mypassword');
define('DB_NAME',       'mydatabase');

Step 7: (Optionally) Setup Files Uploads

Directus supports different storage adapters, by default all files are uploaded to /media directory on the server local filesystem.

Under api/configuration.php there's a filesystem array key-value where you configure your storage. Read more

Step 8: Done!

Open directus by navigating to the path where directus was installed in your local host.

If Directus was installed manually, log in using the default user and password password

Config & Configuration Files

These files are automatically generated during the installation process, alternatively you can utilize the sample files to setup manually.


This file contains project-specific constants for the Directus framework. These values are typically not changed after initial setup.


This file contains the configuration arrays for the Directus framework.

Supported transport:

    'mail' => array(
        'from' => array(
            '' => 'John Doe'
        'transport' => 'mail'
    'mail' => array(
        'from' => array(
            '' => 'John Doe'
        'transport' => 'sendmail'
    'mail' => array(
        'from' => array(
            '' => 'John Doe'
        'transport' => 'smtp',
        'host' => '',
        'username' => 'user',
        'password' => 'pass',
        'port' => '123',

Default Status Mapping

'0' => array(
  'name' => 'Deleted',
  'color' => '#c1272d',
  'sort' => 3
'1' => array(
  'name' => 'Live',
  'color' => '#5b5b5b',
  'sort' => 1
'2' => array(
  'name' => 'Draft',
  'color' => '#bbbbbb',
  'sort' => 2

IP Whitelisting for User-Groups

If your project requires that certain user-groups have access limited to specific IP addresses you can set their directus_groups->restrict_to_ip_whitelist to 1. Then enter any allowed IP addresses (and a brief description) into the directus_ip_whitelist table.

Sidebar Navigation Blacklist for User-Groups

By default, Directus shows all tables that the current user's group has list and view access to. To hide certain tables from this list on a group basis, simply enter a CSV of table names into directus_tab_privileges.directus_tab_blacklist.

Customizing the Sidebar Navigation for User-Groups

By default, Directus displays all available tables alphabetically in the navigation sidebar under the "Tables" group header. To customize this you can build a tailored JSON string and save it within directus_tab_privileges->nav_override for the desired user group.

    "Office": {
        "Staff": {
            "path": "/tables/staff"
        "Locations": {
            "path": "/tables/locations"
    "Portfolio": {
        "Projects/Work": {
            "path": "/tables/projects"
        "Clients (Brands)": {
            "path": "/tables/clients"

Custom Data Workflow

Tables containing a status column (default column name of active) track the publish state of their items/records. Initially, options of Live(1), Draft(2), and Deleted(0) are all options for all tables with a status column. These options, and their associated display-color and saved-value, are editable and extendable within the configuration.php file.

Furthermore, all table permissions for user-groups can be assigned globally or for specific status states. The following should outline this extensive level of customization:

To create a custom data workflow for your users simply set different permissions for each individual status state. This will allow you to restrict which status options are available to users, when they're editing an item of a given state.

NGINX Configuration

At this point it is assumed that you already have installed and configured nginx and have a basic understanding of nginx.

This is an incomplete/rough guide, but it should help you get Directus installed and set up on nginx. It was tested on: Ubuntu 14.04.1 - nginx/1.4.6 (Ubuntu)

See the Directus nginx configuration

Site configuration

Since nginx doesn't use .htaccess, which does the url rewriting, we therefore need to do the rewrite within nginx itself. Below we configure an nginx server block.

Open the default server block file:

sudo vim /etc/nginx/sites-available/default

The nginx server block should look like this:

server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    root /path/to/directus;
    index index.php index.html index.htm;

    server_name localhost;

    location / {
        try_files $uri $uri/ =404;

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;

IMPORTANT: Some NGINX packages are distributed without SCRIPT_FILENAME param inside their fastcgi_params to avoid problems please add fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; before include fastcgi_params;.

Change location / to this:

location / {
    try_files $uri $uri/ /index.php$args;

Add a new location:

location /api {
    if (!-e $request_filename) {
        rewrite ^/1/extensions/([^/]+) /api/api.php?run_extension=$1 last;
    rewrite ^ /api/api.php?run_api_router=1 last;

Add another location to prevent uploaded php/html files to execute.

location ~ ^/(media|storage)/.*\.(php|phps|php5|htm|shtml|xhtml|cgi.+)?$ {
    add_header Content-Type text/plain;


To prevent direct access to extensions api.php file we need to edit /etc/nginx/sites-available/default and add a new location:

location ~* [^/]+/customs/extensions/api\.php$ {
    return 403;

Custom endpoints

Prevent direct access to api endpoint files.

location ~* /customs/endpoints/ {
    deny all;

Fonts Mime types

To serve fonts mime types correctly we need to edit /etc/nginx/mime.types and add these lines in it:

application/    eot;
font/truetype                    ttf;
font/opentype                    otf;
font/woff                        woff;

PHP Values [TODO]

The result of these two files (/etc/nginx/mime.types, /etc/nginx/sites-available/default) and can be found in here config/nginx

Vagrant Box Install



Download and install VirtualBox

Download and install Vagrant

Clone our Vagrant configuration:

git clone

Or download the zip file

$ cd [vagrant dir]
$ vagrant up

What's on the server?


The server now can be access from:

Privileges & Access Control List (ACL)

The access control layer was built in response to the need to maintain the privacy and integrity of certain pieces of data within the database, as managed by Directus. This functionality allows Directus administrators to prevent certain groups from reading and writing to specific fields, and from performing certain operations on given tables.

The Basics

Each row within directus_privileges determines the table permissions (table_name) for a user group (group_id). Optionally, you can increase the fidelity of your privileges by setting the status_id to a an allowed status integer. For instance, you can define a user-group's view privileges for a table specifically when the status of a record is in draft mode. This might be useful if you'd like your "intern" user-group to only see draft content (not live/published).

Field-Level Control

The read_field_blacklist functions by omitting the given column from the database schema which the API yields, such that the front-end app has no awareness of this column. The write_field_blacklist prevents that user group from writing to this column.


The nav_listed boolean is used to hide certain tables from the Directus sidebar and table listing. In certain cases you may want records to be visible throughout the CMS (relationally or otherwise) but you don't need that table listed prominently otherwise. You can also use directus_groups.nav_override to achieve similar results, but for simply hiding a few tables that might be overkill.


The allow_[permission] columns determine which operations the group may perform on this table. Possible values are: 0 (not allowed), 1 (allowed for content you created), 2 (allowed for content anyone created). To differentiate between who created content (and take advantage of these granular privileges), you must set directus_tables.user_create_column to the field within that table which will track the Directus User-ID of the creator.

Access Control List (ACL)

Schema Guide

This page provides insight into the core tables containing all system information, allowing your project data to remain decoupled and unsullied by Directus. Therefore at any point these core tables could be deleted and only your pure database content would remain.

Custom Tables & Fields

The fundamental purpose of Directus is to allow developers to design and create schemas specifically around the needs of their project's scale, performance, architecture, and extensibility. While Directus tries to make no assumptions about your data, there are a few standards which (as of now) should be followed when creating new tables and columns:


This table stores all changes made to item's through Directus. It stores a complete item snapshot, the changes, editor, and other information used in the item history.


This table stores all of the left-nav bookmarks for Directus. This includes bookmarks that users create as well as the "System" bookmarks at the bottom. Each record is assigned to a specific user.


This tables stores all the info about columns managed by Directus. Namely UI, relationships, and other metadata.


This table stores the metadata for all files uploaded through Directus. Because files are an integral part of the framework and its interfaces, Directus does not currently support using other tables to manage files. However, you can create your own "File" tables as a "wrapper" by using the Single/Multiple File UIs.


This table stores the User Groups for the Directus privileges system.


This table stores all user/group messages and item comments used by the framework.


This table tracks all the recipients of each message to know if the message was "read" or not.


This table stores all of the individual user preferences which allows a user's experience to remain the same between sessions/devices.


Each row on this table is associated with a table (table_name) and a Directus user group (group_id). Optionally, you can increase the fidelity of your privileges by setting the status_id to a an allowed status integer. For instance, you can define a user-group's view privileges for a table specifically when the status of a record is in draft mode. This might be useful if you'd like your "intern" user-group to only see draft content (not live/published).

The read_field_blacklist functions by omitting the given column from the database schema which the API yields, such that the front-end app has no awareness of this column. The write_field_blacklist prevents that user group from writing to this column.

The nav_listed boolean is used to hide certain tables from the Directus sidebar and table listing. In certain cases you may want records to be visible throughout the CMS (relationally or otherwise) but you don't need that table listed prominently otherwise. You can also use directus_groups.nav_override to achieve similar results, but for simply hiding a few tables that might be overkill.

The allow_[permission] columns determine which operations the group may perform on this table. Possible values are: 0 (not allowed), 1 (allowed for content you created), 2 (allowed for content anyone created). To differentiate between who created content (and take advantage of these granular privileges), you must set directus_tables.user_create_column to the field within that table which will track the Directus User-ID of the creator.


This system table tracks your current Directus version and the database schema migrations you have run to ensure your database architecture is up to date.


This table stores global Directus settings for this instance in name-value pairs.


This is where system information is stored about all tables managed by Directus.


This tables stores all of the global options/settings for the column User Interfaces (UIs). Since you can change UIs, you may have values within this table that are no longer used – however this allows switching between UIs without losing the saved options therein.


All the users, including admins, are added within this table. Each user is assigned to a single User Group (directus_groups), and that group determines the privileges (directus_privileges) for all of its users.

Manually Setting Passwords

If you need to manually reset a user's password directly in the database you can use the following SQL snippet. Remember to replace the id value below with the actual ID of the user you wish to update.

UPDATE `directus_users` 
SET `salt` = SHA1(RAND()), `password` = SHA1(CONCAT(salt, 'new-password-here'))
WHERE `id` = 1

Directus Folder Structure & Key Files

This page gives a high-level overview of the files and folders of Directus.


Contains all server-side and lightly-abstracted database code.


Contains all client-side application code.


Contains all referenced less, js, css, font, and image files. The unbranded aspect of Directus means there should be no need to edit anything in this folder. All custom css and javascript should live within proprietary UIs, extensions, or list-views (see below).






Contains any proprietary extensions your project may use. Extensions are a catch-all for CMS functionality, if it can't be handled by Directus core or UIs, then extensions are a sandbox for any Each extension present here is automatically loaded into the system unless prepended with an underscore ("_example"). Extensions are self contained, so all templates, logic, etc is within its own folder – for initial setup use the provided example as a guideline... just remember to remove the prepended underscore to activate.


Contains all the necessary files to install and setup Directus. Once completed, this entire folder can and should be removed.


Contains custom templates for proprietary item listing pages. As a primer, reference the core list views within app/modules/xxx/views until a proper example list view is included.






Contains custom UIs (user interfaces) which are used as the inputs for data within the framework. Proprietary UIs in this folder are supplementary to the core UIs located within: app/core-ui – though the file structure/syntax is identical other than location. An exampleui.js file is included, but for more a more comprehensive understanding the core UIs should be perused. Any UIs added to this folder will be immediately available after refreshing the framework.

Extending Directus

Custom User-Interfaces (UIs)

Similar to extensions, if you need an input more specific to your application than provided by core Directus, you can duplicate, tweak, or create your own. Need a custom room-mapping interface or a proprietary client input? Just create a quick custom UI and it will immediately be available for use. [todo]

Custom Extensions

By design, Directus keeps things simple. Only key features utilized by more than 80% of users make it into the core software. Anything not covered under the base suite is possible within extensions. Extensions take advantage of the system’s authentication, ACL, and data connections but force absolutely no restrictions. These sandboxed areas can be used to create custom interfaces, data-visualizations, interaction points, dashboards, point-of-sales, or anything else required by your project. [todo]

Custom API Endpoints

If you’re using Directus' API endpoints (possibly feeding a mobile app or providing a SASS interface) you can always write custom endpoints to compliment the core data/schema/user access. [todo]

Custom File Storage Adapters

Beyond local storage, Directus comes with the ability to connect to a number of popular file storage systems such as CDNs, Amazon S3, Rackspace, Azure and Dropbox.

As with most things within Directus, if you need something more specific the tools exist to easily implement a custom adapter. Directus uses Flysystem, you can read how to create new custom adaptes on their site.

Storage Adapters enable a Directus instance to configure the destination for writing and reading the files which is loaded into the application. Within the configuration.php file, they are defined by the filesystem attribute. (Currently there is not an application-level interface to view or edit these.)

As of writing, these are the adapters can be used out of the box:

These are the supported adapters (Installation required):

@TODO (Supporting)

This adapter is planned for an upcoming release.

These are also available (Installation required):

These are the ones listed on the flysystem page, if the one you are looking for is not listed above, it can be found on GitHub, otherwise you have to create a custom one.

All adapters have different configurable parameters.

'filesystem' => [
    // adapter name
    'adapter' => 'local',
    // By default media directory are located at the same level of directus root
    // To make them a level up outsite the root directory
    // use this instead
    // Ex: 'root' => realpath(BASE_PATH.'/../storage/uploads'),
    // Note: BASE_PATH constant doesn't end with trailing slash
    'root' => BASE_PATH . '/storage/uploads',
    // This is the url where all the media will be pointing to
    // here all assets will be (yourdomain)/storage/uploads
    // same with thumbnails (yourdomain)/storage/uploads/thumbs
    'root_url' => '/storage/uploads',
    'root_thumb_url' => '/storage/uploads/thumbs',
    //   'key'    => 's3-key',
    //   'secret' => 's3-key',
    //   'region' => 's3-region',
    //   'version' => 's3-version',
    //   'bucket' => 's3-bucket'

Installing an adapter

Installing a new adapter using composer can be done executing the command below in a terminal:

Ex: Installing Amazon S3 v3 Adapter.

composer require league/flysystem-aws-s3-v3

You can read on each adapter GitHub page on how to install each one if you are having issues related to the installation process.

Code samples

When building out Directus core functionality and derivative client functionality, it may be necessary to invoke the storage adapter component. Listed below are some references to code samples which demonstrate various ways of deploying storage adapters.

These links point directly to the commit hash which is current as of this writing, so that line numbers do not move out of sync with the documentation. Remember to compare these code snippets with the current state of Directus code in the future, and to update these docs if necessary.


The definition of the upload route shows the most generic (core) way of implementing the storage adapter interface. We instantiate a new \Directus\Media\Storage\Storage object and then simply call the acceptFile method with the incoming local file. The interface automatically fetches the default/thumbnail adapters and passes this information to them.


The authenticated media proxy front controller uses a slightly more complex implementation. After identifying the Directus Media record being requested, it fetches the record for the corresponding storage adapter. It then passes this record to the method \Directus\Media\Storage\Storage::getStorage, which returns the corresponding adapter object, allowing the front controller to fetch the file contents abstractly, irrespective of the underlying driver / file location.


This implementation uses the same approach as the authenticated media front controller, however it performs more complex operations, such as checking if a file exists, and writing a file to the adapter.


Hooks allow project-specific (non-Directus core) code to interact directly with Directus core events. The two hooks which are currently supported are postUpdate and postInsert, which enable logic to react to Directus record update and insert events, respectively. The hooks are very simple to implement: just add custom logic to the (untracked) config file at /directus/api/configuration.php. See this example configuration file as a reference.


Hooks allow you to hook function to be called in a specific time during the executing of Directus.

Hooks list

Name Description
application.boot When the API start booting. Before all endpoints are being set. app object is passed.
application.init API has booted but the endpoint hasn't been dispatched. app object is passed.
application.shutdown API has ran and it's about to shutdown. app object is passed.
application.error An Error has occurred. Exception object is passed.
directus.authenticated User has been authenticated. app object and user info (array) is passed.
directus.authenticated.token User has been authenticated through the API. app object and user info (array) is passed.
directus.authenticated.admin User has been authenticated through the Web App. app object and user info (array) is passed.
directus.start User has been authenticated and API is about to start. app object is passed.
table.create:before Before a table is being created. Table name passed.
table.create:after After a table has been created. Table name passed.
table.create Alias for table.create:after
table.drop:before Before a table is being dropped. Table name is passed.
table.drop:after After a table has been dropped. Table name is passed.
table.drop Alias for table.drop:after
table.insert:before Before new record is being added to a table. Table name and record data passed.
table.insert:after After new record has been added to a table. Table name and record data inserted passed.
table.insert Alias for table.insert:after
table.insert.[table-name]:before Before new record is being added to [table-name]. Record data passed.
table.insert.[table-name]:after After new record has been added to [table-name]. Record data inserted passed.
table.insert.[table-name] Alias for table.insert.[table-name]:after
table.update:before Before a table record is being updated. Table name and record data passed.
table.update:after After a table record has been updated. Table name and record data passed.
table.update Alias for table.update:after
table.update.[table-name]:before Before a [table-name] record is being updated. Record data passed.
table.update.[table-name]:after After a [table-name] record has been updated. Record data passed.
table.update.[table-name] Alias for table.update.[table-name]:after
table.delete:before Before a table record is being deleted. Table name passed.
table.delete:after After a table record has been deleted. Table name.
table.delete Alias for table.delete:after
table.delete.[table-name]:before Before a [table-name] record is being deleted.
table.delete.[table-name]:after After a [table-name] record has been deleted.
table.delete.[table-name] Alias for table.delete.[table-name]:after
files.saving Before a file is being save. Filename and size is passed.
files.saving:after After a file has been saved. Filename and size is passed.
files.thumbnail.saving Before a file thumbnail is being saved. Filename and size is passed.
files.thumbnail.saving:after After a file thumbnail has been saved. Filename and size is passed.
files.deleting Before a file is being deleted. File info is passed.
files.deleting:after After a file has been deleted. File info is passed.
files.thumbnail.deleting Before a file thumbnail is being deleted. File info is passed.
files.thumbnail.deleting:after After a file thumbnail has been deleted. File info is passed.
directus.index.start Index page starts.
directus.login.start Login page starts.

Directus CLI

Introduction and Help

Directus Command-Line Interface (CLI) provides commands that allow you to perform various tasks such as installation, resetting a user's email, or upgrading the database to the most recent Directus schema.

You can use the help command at any time to learn about available CLI actions:

# this will provide information about the current modules
php bin/directus help

To get more information on an specific command you can type "help" followed by the command:

# this provide information about the **install** module
php bin/directus help install

Install Module

Includes commands to install and configure Directus.

Configure Directus:

Creates the config.php and configuration.php files.

IMPORTANT: This command will overwrite any existing config.php and configuration.php files.

php bin/directus install:config -h <db_host> -n <db_name> -u <db_user> -p <db_pass> -d <directus_path>

Example: http://example.local

php bin/directus install:config -h localhost -n directus -u root -p pass

Example: http://example.local/directus

php bin/directus install:config -h localhost -n directus -u root -p pass -d directus

Populate the Database Schema:

Creates all of the Directus Core tables based on the configuration files: /api/config.php and /api/configuration.php.

php bin/directus install:database

Install Initial Configurations:

Create the default admin user and the site's default settings.

php bin/directus install:install -e <admin_email> -p <admin_password> -t <site_name>


php bin/directus install:install -e admin@directus.local -p password -t "Directus Example"

User Module

Includes commands to manage Directus users

Change User Password:

php bin/directus user:password -e <user_email> -p <new_password>


php bin/directus user:password -e admin@directus.local -p newpassword

Database Module

Includes commands to manage Directus database schema

Note: This require that Directus has a valid connection configured in api/config.php. Read more

IMPORTANT: Always backup your database before run the database module to prevent data loss.

Install Directus schema:

php bin/directus db:install

Upgrade Directus schema:

php bin/directus db:upgrade

Language Module

Includes commands to manage Directus languages files

See all the missing locale keys compared to English:

php bin/directus language:diff


Bug Reporting and Feature Requests

Report bugs directly on GitHub Issues – request and vote for new features on FeatHub. For all security related issues, please chat with us directly through

Server Error: Automatically populating $http_raw_post_data is deprecated

Within PHP 5.6.x $HTTP_RAW_POST_DATA is deprecated, but sometimes isn't on individual installs (php bug #66763)

To solve this add/use/update this on your php.ini always_populate_raw_post_data = -1

Server error occured!

If you get "Server error occured!" the first time you try to login, it likely means that you missconfigured Apache2. Try adding AllowOverride All into your virtualHost.

MySQL Strict Mode

Directus does not fully support Strict Mode due to limitations with the PDO and MySQL Drivers. Please disable MySQL Strict Mode before installation.

You can check to see if your database is in strict mode with the following queries:

SELECT @@GLOBAL.sql_mode;
SELECT @@SESSION.sql_mode;

How to Enable mod_rewrite


  1. In the terminal run a2enmod rewrite
  2. Restart apache2 with /etc/init.d/apache2 restart or service apache2 restart

Windows (WAMP)

  1. wamp tray icon > apache > apache module > rewrite_module


  1. Click on modules tab.
  2. Look for and check rewrite_module.

mod_rewrite is enabled and still not working

  1. Go to your apache configuration (httpd.conf or apache.conf)
  2. Look for the <Directory> directive that matches the Directus path.


    <Directory "/var/www/html">
        AllowOverride All
        Order allow,deny
        Allow from all
  3. Add AllowOverride All, If AllowOverride None exists change None to All to allow .htaccess files.

mod_rewrite is enabled and still getting 404 error.

If you are using VirtualDocumentRoot RewriteBase needs to be set.

  1. Go to /directus/path/.htaccess and add RewriteBase / just below RewriteEngine On.
  2. Go to /directus/path/api/.htaccess and add RewriteBase /api just below RewriteEngine On.

How do I reset a password manually?

This section of the Docs explains how to update a password with a SQL command.