Setting up a Craft Project from Scratch

While we recommend using the official starter project, there are only a couple of things you need to know to start completely from scratch. This guide is intended for advanced users and tinkerers who want to experiment with how Craft is initialized.

Please be aware that downloading or installing Craft binds you to its license.

Initializing with Composer #

All projects will involve Composer at some point—so we might as well get started on the right foot, with the init command:

mkdir my-custom-project
cd my-custom-project
composer init

Skip or accept defaults for all the prompts, except for these:

  • Package Typeproject
  • Would you like to define your dependencies (require) interactively?y for “Yes”
  • Search for a package:craftcms/cms
    Allow Composer to find the latest version. Press Enter when prompted to search again to exit dependency declaration, and answer no when prompted to declare require-dev dependencies.
  • Add PSR-4 autoload mapping?n to skip
  • Do you trust yiisoft/yii2-composer to execute code and wish to enable it now?y for “Yes”
  • Do you trust craftcms/plugin-installer to execute code and wish to enable it now?y for “Yes”

You will be left with a fresh vendor/ directory and a composer.json file that looks something like this:

    "name": "me/my-custom-project",
    "require": {
        "craftcms/cms": "^5.1"
    "authors": [
            "name": "My Name",
            "email": ""
    "config": {
        "allow-plugins": {
            "yiisoft/yii2-composer": true,
            "craftcms/plugin-installer": true

Comparing this to the starter kit, you’ll notice it’s pretty similar.

Bootstrapping Craft #

Now that Craft’s source files are installed, you’ll need to create “entry scripts” for web and console requests.

├── vendor/
│   └── ...
├── web/
│   ├── .htaccess
│   └── index.php     ← (1) Web
│   bootstrap.php     ← (3) Bootstrap
├── composer.json
├── composer.lock
└── craft             ← (2) Console

Create a web/ directory, and two files inside it: index.php (1), to serve web requests…


require dirname(__DIR__) . '/bootstrap.php';

/** @var craft\web\Application $app */
$app = require CRAFT_VENDOR_PATH . '/craftcms/cms/bootstrap/web.php';

…and—if you plan on using Apache—a boilerplate .htaccess file that reroutes any request that doesn’t match a “real” file through index.php:

<IfModule mod_rewrite.c>
    RewriteEngine On

    # Send would-be 404 requests to Craft
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} !^/(favicon\.ico|apple-touch-icon.*\.png)$ [NC]
    RewriteRule (.+) index.php?p=$1 [QSA,L]

Note that index.php contains a reference to a shared “bootstrap” file in the directory above it. We’ll create that in a moment.

The web/ directory can be named whatever you like, so long as you can configure your web server to use it as the web root. See Moving Craft’s Files Below the Web Root for some hints on how to customize the folder structure.

Now, at the root level of your project, create a craft (3) file (no extension), taking care to include the special “shebang” line:

#!/usr/bin/env php

require __DIR__ . '/bootstrap.php';

/** @var craft\console\Application $app */
$app = require CRAFT_VENDOR_PATH . '/craftcms/cms/bootstrap/console.php';
$exitCode = $app->run();

This console entry script also refers to the bootstrap.php (3) file, which also goes in the project’s root:


// Two constants inform much of Craft’s ability to find resources in your project:
define('CRAFT_BASE_PATH', __DIR__);
define('CRAFT_VENDOR_PATH', CRAFT_BASE_PATH . '/vendor');

// Load Composer's autoloader
require_once CRAFT_VENDOR_PATH . '/autoload.php';

As a means of DRYing up the entry scripts, this is where the starter project loads environment variables—and where we recommend performing some low-level bootstrap configuration.

Environment Variable Support #

The official starter project automatically supports defining environment variables from a .env file, through the vlucas/phpdotenv package.

It’s up to you how to manage secrets! If your development and production environments have built-in tools for this, you may not need to implement a .env loader. Regardless of how you choose to set environment variables, Craft will be able to get them without any external support.

If at this point you are inclined to implement .env support, take another look at the starter project. It will likely end up being the minimal scaffold you are looking for, and is still completely customizable after initialization!

Configuration #

Craft looks for configuration files in the config/ directory. license.key will be written to this folder automatically, but general, database, and application config files are your responsibility.

Your configuration should complement your approach to secret management. Craft provides helper functions like App::env('...') and App::parseEnv('...') to deal with resolving values at runtime, which are preferred over hard-coding values or ignoring config files altogether.

Other Directories #

Craft relies on a storage/ directory at the root of your project. Consider tracking the directory but ignoring backups, composer-backups, config-backups, logs, and runtime, within it.

Similarly, the web/cpresources/ directory is necessary for the control panel to work, but everything inside it can be ignored.

Review the starter project and directory structure documentation to determine if any additional directories are required.

Ignoring Files #

The minimum .gitignore file should look something like this:


Other Considerations #

  • Configuration makes it possible to keep secrets out of your config/ directory, so it can be usually be tracked safely;
  • config/license.key and plugin license keys can stay with your project, as they will be bound to your Craft ID once purchased;
  • Keeping a .env.example is a helpful practice, as it lets other developers know what configuration is required to make your project work;
  • Review our recommendations for how to handle the contents of the storage/ directory;
  • Other directories in the starter project may include .gitignore files of their own;

Never version-control files that contain sensitive information. If .env or another sensitive file makes it into your repo, keys should be rotated immediately.

Applies to Craft CMS 5 and Craft CMS 4.