diff --git a/composer.json b/composer.json
index be6f07a..92eee0d 100644
--- a/composer.json
+++ b/composer.json
@@ -13,7 +13,8 @@
     "require": {
         "loomlabs/utility.collection": "^1.0",
         "loomlabs/utility.filepath": "^1.1",
-        "symfony/console": "^7.2"
+        "symfony/console": "^7.2",
+        "symfony/yaml": "^7.2"
     },
     "require-dev": {
         "phpunit/phpunit": "^12.1"
diff --git a/config/.template.env b/config/.template.env
new file mode 100644
index 0000000..60fecf1
--- /dev/null
+++ b/config/.template.env
@@ -0,0 +1,3 @@
+PROJECT_DIRECTORY=%s
+PROJECT_NAME=%s
+PHP_VERSION=%s
\ No newline at end of file
diff --git a/config/docker-compose.sample.yaml b/config/docker-compose.sample.yaml
new file mode 100644
index 0000000..f8ed8ff
--- /dev/null
+++ b/config/docker-compose.sample.yaml
@@ -0,0 +1,26 @@
+services:
+#  nginx:
+#    build:
+#      context: ./nginx
+#    ports:
+#      - ${NGINX_PORT}:80
+#    volumes:
+#      - ${PROJECT_DIRECTORY}:/var/www/html:cached
+#      - ./nginx/conf.d:/etc/nginx/conf.d
+#  database:
+#    image: mysql:8.0
+#    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
+#    ports:
+#      - ${MYSQL_PORT}:3306
+#    expose:
+#      - "3306"
+#    environment:
+#      MYSQL_ROOT_PASSWORD: docker
+#    volumes:
+#      - ./data/${PROJECT_NAME}/mysql:/var/lib/mysql:cached
+#  cache:
+#    image: redis:latest
+#    ports:
+#      - ${REDIS_PORT}:6379
+#    volumes:
+#      - ./data/${PROJECT_NAME}/redis:/data:cached
\ No newline at end of file
diff --git a/config/docker-compose.yml b/config/docker-compose.yml
deleted file mode 100644
index 7cc0ae3..0000000
--- a/config/docker-compose.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-version: "3.9"
-
-services:
-  nginx:
-    build:
-      context: ./nginx
-    ports:
-      - ${NGINX_PORT}:80
-    volumes:
-      - ${PROJECT_DIRECTORY}:/var/www/html:cached
-      - ./nginx/conf.d:/etc/nginx/conf.d
-  php:
-    build:
-      context: ./php-fpm
-    working_dir: /var/www/html
-    extra_hosts:
-      - host.docker.internal:host-gateway
-    volumes:
-      - ${PROJECT_DIRECTORY}:/var/www/html:cached
-    ports:
-      - ${PHP_PORT}:9003
-  database:
-    image: mysql:8.0
-    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
-    ports:
-      - ${MYSQL_PORT}:3306
-    expose:
-      - "3306"
-    environment:
-      MYSQL_ROOT_PASSWORD: docker
-    volumes:
-      - ./data/${PROJECT_NAME}/mysql:/var/lib/mysql:cached
-  cache:
-    image: redis:latest
-    ports:
-      - ${REDIS_PORT}:6379
-    volumes:
-      - ./data/${PROJECT_NAME}/redis:/data:cached
\ No newline at end of file
diff --git a/config/php-fpm/Dockerfile b/config/php-fpm/Dockerfile
new file mode 100644
index 0000000..b4a1759
--- /dev/null
+++ b/config/php-fpm/Dockerfile
@@ -0,0 +1,41 @@
+ARG PHP_VERSION
+
+ENV PHP_VERSION ${PHP_VERSION}
+
+FROM php:${PHP_VERSION}-fpm
+
+RUN mkdir -p /var/www/html
+
+RUN apt-get -qq update && apt-get -qq install -y \
+    apt-transport-https \
+    git \
+    curl \
+    zip \
+    unzip \
+    libicu-dev \
+    imagick \
+    nano \
+    bash \
+    dnsutils
+
+ENV NVM_DIR /root/.nvm
+ENV NODE_VERSION 20
+
+RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash \
+    && . "$NVM_DIR/nvm.sh" \
+    && nvm install $NODE_VERSION \
+    && nvm use $NODE_VERSION
+
+COPY --from=node:20 /usr/local/bin/npx /usr/local/bin/npx
+
+RUN docker-php-ext-configure intl > /dev/null
+RUN docker-php-ext-install mysqli pdo pdo_mysql sockets intl exif bcmath opcache > /dev/null
+
+RUN pecl install xdebug redis \
+    && docker-php-ext-enable redis
+
+COPY ./xdebug.ini.tmp "${PHP_INI_DIR}/conf.d/xdebug.ini"
+COPY ./opcache.ini "${PHP_INI_DIR}/conf.d"
+COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
+
+WORKDIR /var/www/html
\ No newline at end of file
diff --git a/config/php.yaml b/config/php.yaml
new file mode 100644
index 0000000..b99d0bd
--- /dev/null
+++ b/config/php.yaml
@@ -0,0 +1,10 @@
+php:
+  build:
+    context: ./php-fpm
+  working_dir: /var/www/html
+  extra_hosts:
+    - host.docker.internal:host-gateway
+  volumes:
+    - ${PROJECT_DIRECTORY}:/var/www/html:cached
+  ports:
+    - ${PHP_PORT}:9003
\ No newline at end of file
diff --git a/config/spinner.yaml b/config/spinner.yaml
new file mode 100644
index 0000000..079f3c6
--- /dev/null
+++ b/config/spinner.yaml
@@ -0,0 +1,4 @@
+options:
+  environment:
+    php:
+      version: 8.4
\ No newline at end of file
diff --git a/data/.gitignore b/data/.gitignore
new file mode 100644
index 0000000..c96a04f
--- /dev/null
+++ b/data/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
\ No newline at end of file
diff --git a/src/Classes/File/SpinnerFilePath.php b/src/Classes/File/SpinnerFilePath.php
index c148cf8..a95e8a7 100644
--- a/src/Classes/File/SpinnerFilePath.php
+++ b/src/Classes/File/SpinnerFilePath.php
@@ -8,8 +8,13 @@ use Loom\Utility\FilePath\FilePath;
 
 class SpinnerFilePath extends FilePath
 {
-    public function __construct(string $projectPath)
+    public function __construct(string $path)
     {
-        parent::__construct(sprintf('%s%s%s', dirname(__DIR__, 3), DIRECTORY_SEPARATOR, $projectPath));
+        parent::__construct(sprintf('%s%s%s', dirname(__DIR__, 3), DIRECTORY_SEPARATOR, $path));
+    }
+
+    public function getProvidedPath(): string
+    {
+        return $this->path;
     }
 }
\ No newline at end of file
diff --git a/src/Command/AbstractSpinnerCommand.php b/src/Command/AbstractSpinnerCommand.php
index 27a654c..460395b 100644
--- a/src/Command/AbstractSpinnerCommand.php
+++ b/src/Command/AbstractSpinnerCommand.php
@@ -8,11 +8,11 @@ use Loom\Spinner\Classes\Collection\FilePathCollection;
 use Loom\Spinner\Classes\File\SpinnerFilePath;
 use Loom\Spinner\Classes\OS\System;
 use Loom\Spinner\Command\Interface\ConsoleCommandInterface;
-use Loom\Utility\FilePath\FilePath;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Output\OutputInterface;
 use Symfony\Component\Console\Style\SymfonyStyle;
+use Symfony\Component\Yaml\Yaml;
 
 class AbstractSpinnerCommand extends Command implements ConsoleCommandInterface
 {
@@ -34,6 +34,8 @@ class AbstractSpinnerCommand extends Command implements ConsoleCommandInterface
     {
         $this->setStyle($input, $output);
 
+        $this->style->title('Loom Spinner');
+
         if (!$this->system->isDockerEngineRunning()) {
             $this->style->error('It looks like the Docker Engine is not running. Please start it and try again.');
 
@@ -41,7 +43,7 @@ class AbstractSpinnerCommand extends Command implements ConsoleCommandInterface
         }
 
         if ($input->hasArgument('path')) {
-            $projectDirectory = new FilePath($input->getArgument('path'));
+            $projectDirectory = new SpinnerFilePath($input->getArgument('path'));
 
             if (!$projectDirectory->exists() || !$projectDirectory->isDirectory()) {
                 $this->style->error('The provided path is not a valid directory.');
@@ -52,9 +54,58 @@ class AbstractSpinnerCommand extends Command implements ConsoleCommandInterface
             $this->filePaths->add($projectDirectory, 'project');
         }
 
+        if ($input->hasArgument('name')) {
+            $this->filePaths->add(
+                new SpinnerFilePath(sprintf('data/environments/%s', $input->getArgument('name'))),
+                'projectData'
+            );
+            $this->filePaths->add(
+                new SpinnerFilePath(sprintf('data/environments/%s/.env', $input->getArgument('name'))),
+                'projectEnv'
+            );
+        }
+
         return Command::SUCCESS;
     }
 
+    /**
+     * @throws \Exception
+     */
+    protected function getDefaultConfig()
+    {
+        if (!$this->filePaths->get('defaultSpinnerConfig')?->exists()) {
+            throw new \Exception('Default spinner configuration file not found.');
+        }
+
+        return Yaml::parseFile($this->filePaths->get('defaultSpinnerConfig')->getAbsolutePath())['options'] ?? null;
+    }
+
+    protected function buildDockerComposeCommand(string $command, bool $daemon = true): string
+    {
+        return sprintf(
+            'cd %s && docker compose %s --env-file=%s%s',
+            $this->filePaths->get('config')->getAbsolutePath(),
+            $command,
+            $this->filePaths->get('projectEnv')->getAbsolutePath()
+                ?? $this->filePaths->get('projectEnv')->getProvidedPath(),
+            $daemon ? ' -d' : ''
+        );
+    }
+
+    /**
+     * @throws \Exception
+     */
+    protected function getDefaultPhpVersion(): ?float
+    {
+        if (!$this->filePaths->get('defaultSpinnerConfig')?->exists()) {
+            throw new \Exception('Default spinner configuration file not found.');
+        }
+
+        $config = $this->getDefaultConfig();
+
+        return $config['environment']['php']['version'] ?? null;
+    }
+
     private function setStyle(InputInterface $input, OutputInterface $output): void
     {
         $this->style = new SymfonyStyle($input, $output);
@@ -64,6 +115,9 @@ class AbstractSpinnerCommand extends Command implements ConsoleCommandInterface
     {
         $this->filePaths = new FilePathCollection([
             'config' => new SpinnerFilePath('config'),
+            'defaultSpinnerConfig' => new SpinnerFilePath('config/spinner.yaml'),
+            'envTemplate' => new SpinnerFilePath('config/.template.env'),
+            'data' => new SpinnerFilePath('data'),
         ]);
     }
 }
\ No newline at end of file
diff --git a/src/Command/SpinCommand.php b/src/Command/SpinCommand.php
index b19234a..995934a 100644
--- a/src/Command/SpinCommand.php
+++ b/src/Command/SpinCommand.php
@@ -8,24 +8,49 @@ use Symfony\Component\Console\Attribute\AsCommand;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputArgument;
 use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Output\OutputInterface;
 
-#[AsCommand(name: 'spin', description: 'Spin up a new development environment')]
+#[AsCommand(name: 'spin:up', description: 'Spin up a new development environment')]
 class SpinCommand extends AbstractSpinnerCommand
 {
+    /**
+     * @throws \Exception
+     */
     protected function configure(): void
     {
         $this
             ->addArgument('name', InputArgument::REQUIRED, 'The name for your Docker container.')
-            ->addArgument('path', InputArgument::REQUIRED, 'The absolute path to your projects root directory.');
+            ->addArgument('path', InputArgument::REQUIRED, 'The absolute path to your projects root directory.')
+            ->addOption('php', null, InputOption::VALUE_REQUIRED, 'The PHP version to use (e.g., 8.0).', $this->getDefaultPhpVersion());
     }
 
     protected function execute(InputInterface $input, OutputInterface $output): int
     {
-        if (!parent::execute($input, $output)) {
+        if (parent::execute($input, $output)) {
             return Command::FAILURE;
         }
 
+        if ($this->filePaths->get('projectData')->exists()) {
+            $this->style->warning('Project data already exists. Skipping new build.');
+
+            return Command::SUCCESS;
+        }
+
+        $this->style->success("Spinning up a new development environment...");
+
+        mkdir($this->filePaths->get('projectData')->getProvidedPath(), 0777, true);
+
+        file_put_contents(
+            $this->filePaths->get('projectEnv')->getProvidedPath(),
+            sprintf(
+                file_get_contents($this->filePaths->get('envTemplate')->getAbsolutePath()),
+                $this->filePaths->get('project')->getAbsolutePath(),
+                $input->getArgument('name'),
+                $input->getOption('php')
+            )
+        );
+
         return Command::SUCCESS;
     }
 }
\ No newline at end of file