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:
- 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. - 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": "^4.3"
},
"authors": [
{
"name": "My Name",
"email": "email@domain.com"
}
],
"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.
my-custom-project/
├── 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…
<?php
require dirname(__DIR__) . '/bootstrap.php';
/** @var craft\web\Application $app */
$app = require CRAFT_VENDOR_PATH . '/craftcms/cms/bootstrap/web.php';
$app->run();
…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]
</IfModule>
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
<?php
require __DIR__ . '/bootstrap.php';
/** @var craft\console\Application $app */
$app = require CRAFT_VENDOR_PATH . '/craftcms/cms/bootstrap/console.php';
$exitCode = $app->run();
exit($exitCode);
This console entry script also refers to the bootstrap.php
(3
) file, which also goes in the project’s root:
<?php
// 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:
/.env
/vendor
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.