Skip to content

Hanra15/podman-laravel

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Laravel with Podman on WSL - Complete Tutorial

A comprehensive guide to setting up Laravel development environment using Podman instead of Docker on WSL (Windows Subsystem for Linux).

Table of Contents


Prerequisites

  • WSL2 installed on Windows
  • Ubuntu or Debian-based Linux distribution on WSL
  • Basic knowledge of Laravel and command line

Why Podman over Docker?

Podman Advantages:

  • Rootless by default - Better security
  • Daemonless architecture - No background service consuming resources
  • Docker-compatible CLI - Almost identical commands
  • Lower resource usage - No daemon means less memory overhead
  • Fully open-source - No licensing concerns
  • Native pod support - Kubernetes-like pod management

When to use Podman:

  • CLI-only workflows
  • Security-focused environments
  • Lightweight development setups
  • Linux-native development

Installation

1. Install Podman

# Update system
sudo apt update && sudo apt upgrade -y

# Install Podman
sudo apt install -y podman

# Verify installation
podman --version

2. Install Podman Compose

Option A: Using pipx (Recommended)

# Install pipx
sudo apt install -y pipx

# Ensure pipx path is set up
pipx ensurepath

# Restart shell or run
source ~/.bashrc

# Install podman-compose
pipx install podman-compose

# Verify
podman-compose --version

Option B: Using Podman's built-in compose

# Check if available
podman compose version

# If available, just use it directly

3. Configure Podman for WSL

# Create config directory
mkdir -p ~/.config/containers

# Configure storage
cat > ~/.config/containers/storage.conf << 'EOF'
[storage]
driver = "overlay"
runroot = "/run/user/1000/containers"
graphroot = "/home/$USER/.local/share/containers/storage"

[storage.options]
mount_program = "/usr/bin/fuse-overlayfs"
EOF

# Configure containers
cat > ~/.config/containers/containers.conf << 'EOF'
[containers]
netns="host"
userns="host"
ipcns="host"
utsns="host"
cgroupns="host"
cgroups="disabled"
log_driver = "k8s-file"

[network]
network_backend = "cni"
EOF

# Configure registries (important!)
sudo tee /etc/containers/registries.conf > /dev/null << 'EOF'
unqualified-search-registries = ["docker.io"]

[[registry]]
prefix = "docker.io"
location = "docker.io"
EOF

4. Set Up Docker Compatibility Aliases

# Add aliases to bashrc
cat >> ~/.bashrc << 'EOF'

# Podman aliases for Docker compatibility
alias docker='podman'
alias docker-compose='podman-compose'
EOF

# Apply changes
source ~/.bashrc

5. Test Installation

# Test Podman
podman run --rm docker.io/library/hello-world

# Test podman-compose
podman-compose --version

Project Setup

Step 1: Create Project Directory

# Navigate to your web root (adjust path as needed)
cd /var/www

# Create project directory
sudo mkdir -p my-laravel-project
sudo chown -R $USER:$USER my-laravel-project
cd my-laravel-project

Step 2: Create Laravel Project

# Create src directory
mkdir -p src

# Create Laravel project using Composer
podman run --rm -v $(pwd)/src:/app -w /app \
  docker.io/library/composer:latest \
  create-project laravel/laravel .

# Fix permissions
sudo chown -R $USER:$USER src

Configuration Files

1. Create Dockerfile for PHP

File: Dockerfile.php

FROM docker.io/library/php:8.2-fpm

# Install system dependencies
RUN apt-get update && apt-get install -y \
    git \
    curl \
    libpng-dev \
    libonig-dev \
    libxml2-dev \
    zip \
    unzip \
    libzip-dev

# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip

# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# Set working directory
WORKDIR /var/www/html

# Set permissions
RUN chown -R www-data:www-data /var/www/html

2. Create Nginx Configuration

File: nginx/default.conf

# Create nginx directory first
mkdir -p nginx
server {
    listen 80;
    server_name localhost;
    root /var/www/html/public;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }

    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
}

3. Create Docker Compose File

File: docker-compose.yml

version: "3.8"

services:
  # Nginx Service
  nginx:
    image: docker.io/library/nginx:alpine
    container_name: laravel-nginx
    restart: unless-stopped
    ports:
      - "8080:80"
    volumes:
      - ./src:/var/www/html
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - php
    networks:
      - laravel-network

  # PHP-FPM Service
  php:
    build:
      context: .
      dockerfile: Dockerfile.php
    container_name: laravel-php
    restart: unless-stopped
    volumes:
      - ./src:/var/www/html
    networks:
      - laravel-network
    environment:
      - DB_HOST=mysql
      - DB_PORT=3306
      - DB_DATABASE=laravel
      - DB_USERNAME=laravel
      - DB_PASSWORD=secret

  # MySQL Service
  mysql:
    image: docker.io/library/mysql:8.0
    container_name: laravel-mysql
    restart: unless-stopped
    # ports:
    #   - "3307:3306"  # Uncomment if you need external access
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_USER: laravel
      MYSQL_PASSWORD: secret
      MYSQL_ROOT_PASSWORD: rootsecret
    volumes:
      - mysql-data:/var/lib/mysql
    networks:
      - laravel-network
    command: --default-authentication-plugin=mysql_native_password

  # PhpMyAdmin
  phpmyadmin:
    image: docker.io/phpmyadmin/phpmyadmin:latest
    container_name: laravel-phpmyadmin
    restart: unless-stopped
    ports:
      - "8081:80"
    environment:
      PMA_HOST: mysql
      PMA_PORT: 3306
      MYSQL_ROOT_PASSWORD: rootsecret
    depends_on:
      - mysql
    networks:
      - laravel-network

networks:
  laravel-network:
    driver: bridge

volumes:
  mysql-data:
    driver: local

4. Configure Laravel Environment

Update src/.env

cat > src/.env << 'EOF'
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost:8080

LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=secret

BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DISK=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120

MEMCACHED_HOST=127.0.0.1

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_MAILER=smtp
MAIL_HOST=mailpit
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
EOF

Running the Project

1. Build and Start Services

# Build PHP image
podman-compose build php

# Start all services
podman-compose up -d

# Check if containers are running
podman ps

Expected output:

CONTAINER ID  IMAGE                              COMMAND               CREATED        STATUS        PORTS                   NAMES
xxxxx         docker.io/library/nginx:alpine     nginx -g daemon o...  2 minutes ago  Up 2 minutes  0.0.0.0:8080->80/tcp    laravel-nginx
xxxxx         localhost/my-laravel-project_php   php-fpm              2 minutes ago  Up 2 minutes                          laravel-php
xxxxx         docker.io/library/mysql:8.0        mysqld               2 minutes ago  Up 2 minutes                          laravel-mysql
xxxxx         docker.io/phpmyadmin/phpmyadmin    apache2-foregroun...  2 minutes ago  Up 2 minutes  0.0.0.0:8081->80/tcp    laravel-phpmyadmin

2. Initialize Laravel

# Generate application key
podman exec -it laravel-php php artisan key:generate

# Wait for MySQL to be ready
sleep 15

# Run migrations
podman exec -it laravel-php php artisan migrate

# Fix permissions
podman exec -it laravel-php chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
podman exec -it laravel-php chmod -R 775 /var/www/html/storage /var/www/html/bootstrap/cache

3. Access Your Application


Common Commands

Container Management

# Start all services
podman-compose up -d

# Stop all services
podman-compose down

# Stop and remove volumes (⚠️ deletes database data)
podman-compose down -v

# Restart all services
podman-compose restart

# Restart specific service
podman-compose restart nginx

# View logs (all services)
podman-compose logs -f

# View logs (specific service)
podman logs -f laravel-nginx
podman logs -f laravel-php
podman logs -f laravel-mysql

# List running containers
podman ps

# List all containers (including stopped)
podman ps -a

Laravel Artisan Commands

# Run any artisan command
podman exec -it laravel-php php artisan [command]

# Common examples:
podman exec -it laravel-php php artisan migrate
podman exec -it laravel-php php artisan migrate:fresh --seed
podman exec -it laravel-php php artisan make:controller UserController
podman exec -it laravel-php php artisan make:model Post -mcr
podman exec -it laravel-php php artisan make:migration create_posts_table
podman exec -it laravel-php php artisan db:seed
podman exec -it laravel-php php artisan cache:clear
podman exec -it laravel-php php artisan config:clear
podman exec -it laravel-php php artisan route:list
podman exec -it laravel-php php artisan tinker

Composer Commands

# Install dependencies
podman exec -it laravel-php composer install

# Install specific package
podman exec -it laravel-php composer require vendor/package

# Update dependencies
podman exec -it laravel-php composer update

# Remove package
podman exec -it laravel-php composer remove vendor/package

# Dump autoload
podman exec -it laravel-php composer dump-autoload

Access Container Shell

# Access PHP container
podman exec -it laravel-php bash

# Access Nginx container
podman exec -it laravel-nginx sh

# Access MySQL directly
podman exec -it laravel-mysql mysql -ularavel -psecret laravel

Database Commands

# Connect to MySQL
podman exec -it laravel-mysql mysql -ularavel -psecret laravel

# Export database
podman exec laravel-mysql mysqldump -ularavel -psecret laravel > backup.sql

# Import database
podman exec -i laravel-mysql mysql -ularavel -psecret laravel < backup.sql

Project Structure

Your final project structure should look like:

my-laravel-project/
├── docker-compose.yml
├── Dockerfile.php
├── nginx/
│   └── default.conf
└── src/                    # Laravel application
    ├── app/
    ├── bootstrap/
    ├── config/
    ├── database/
    ├── public/
    ├── resources/
    ├── routes/
    ├── storage/
    ├── tests/
    ├── .env
    ├── artisan
    ├── composer.json
    └── composer.lock

Troubleshooting

Port Already in Use

Error: bind: address already in use

Solution:

# Check what's using the port
sudo lsof -i :8080
sudo lsof -i :3306

# Stop the service or change port in docker-compose.yml
# For MySQL, change:
ports:
  - "3307:3306"  # Use 3307 instead of 3306

Permission Denied Errors

Solution:

# Fix Laravel storage permissions
podman exec -it laravel-php chown -R www-data:www-data /var/www/html/storage
podman exec -it laravel-php chown -R www-data:www-data /var/www/html/bootstrap/cache
podman exec -it laravel-php chmod -R 775 /var/www/html/storage
podman exec -it laravel-php chmod -R 775 /var/www/html/bootstrap/cache

Database Connection Failed

Solution:

# Ensure MySQL is ready (wait longer)
sleep 20
podman exec -it laravel-php php artisan migrate

# Check MySQL logs
podman logs laravel-mysql

# Verify .env database settings match docker-compose.yml

Container Won't Start

Solution:

# Check logs
podman logs laravel-php
podman logs laravel-nginx
podman logs laravel-mysql

# Remove and rebuild
podman-compose down -v
podman-compose build --no-cache
podman-compose up -d

Registry/Image Pull Issues

Error: short-name did not resolve to an alias

Solution:

# Ensure registries.conf is configured
sudo tee /etc/containers/registries.conf > /dev/null << 'EOF'
unqualified-search-registries = ["docker.io"]

[[registry]]
prefix = "docker.io"
location = "docker.io"
EOF

# Or use fully qualified image names in docker-compose.yml
# Example: docker.io/library/nginx:alpine

Build Fails with COPY --from Error

Solution: Update Dockerfile.php to install Composer using curl:

# Instead of: COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Use:
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

Tips & Best Practices

1. Use .dockerignore

Create .dockerignore in project root:

.git
.env
node_modules
vendor
storage/logs/*
storage/framework/cache/*
storage/framework/sessions/*
storage/framework/views/*

2. Development vs Production

For production, modify docker-compose.yml:

  • Remove PhpMyAdmin
  • Use environment variables
  • Add proper volume backups
  • Use secrets for passwords

3. Database Persistence

Data is stored in named volumes. To backup:

# Backup volume
podman volume export mysql-data > mysql-backup.tar

# Restore volume
podman volume import mysql-data < mysql-backup.tar

4. Performance Optimization

For better performance in WSL:

# In docker-compose.yml, add for volumes:
volumes:
  - ./src:/var/www/html:cached  # Add :cached

5. Multiple Projects

To run multiple Laravel projects simultaneously:

  • Change ports in each docker-compose.yml
  • Use different container names
  • Use different network names

Example for second project:

ports:
  - "8082:80" # Different port
container_name: laravel2-nginx # Different name
networks:
  - laravel2-network # Different network

Quick Start Template

For your next project, use this quick command sequence:

# 1. Create project
cd /var/www
sudo mkdir -p new-project && sudo chown -R $USER:$USER new-project
cd new-project

# 2. Create Laravel
mkdir -p src
podman run --rm -v $(pwd)/src:/app -w /app docker.io/library/composer:latest create-project laravel/laravel .

# 3. Copy configuration files from existing project
cp ../my-laravel-project/docker-compose.yml .
cp ../my-laravel-project/Dockerfile.php .
cp -r ../my-laravel-project/nginx .

# 4. Update .env
cat > src/.env << 'EOF'
# (paste .env content from above)
EOF

# 5. Start
podman-compose build php
podman-compose up -d
sleep 15
podman exec -it laravel-php php artisan key:generate
podman exec -it laravel-php php artisan migrate

Uninstalling Docker (Optional)

If you want to completely remove Docker after switching to Podman:

Remove Docker from WSL

sudo systemctl stop docker
sudo systemctl disable docker
sudo apt remove -y docker docker-engine docker.io containerd runc
sudo apt autoremove -y
sudo rm -rf /var/lib/docker
sudo rm -rf /etc/docker
sudo rm -rf ~/.docker

Remove Docker Desktop from Windows

# In PowerShell (as Administrator)
winget uninstall Docker.DockerDesktop

# Clean up
Remove-Item -Recurse -Force "$env:APPDATA\Docker" -ErrorAction SilentlyContinue
Remove-Item -Recurse -Force "$env:LOCALAPPDATA\Docker" -ErrorAction SilentlyContinue
Remove-Item -Recurse -Force "$env:ProgramData\Docker" -ErrorAction SilentlyContinue

Additional Resources


Conclusion

You now have a complete Laravel development environment running on Podman! This setup is:

  • ✅ Lightweight and fast
  • ✅ Secure (rootless containers)
  • ✅ Docker-compatible
  • ✅ Easy to replicate for new projects

Happy coding! 🚀

About

A tutorial to make laravel project with podman container on WSL

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages