Runtimes Advanced

Troubleshooting Laravel Artisan Migrate ‘SQLSTATE base table or view not found’ on Alpine Linux

Resolve 'SQLSTATE base table or view not found' during Laravel migrations on Alpine Linux. This guide tackles PDO driver issues, database connectivity, and Docker misconfigurations.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Resolve 'SQLSTATE base table or view not found' during Laravel migrations on Alpine Linux. This guide tackles PDO driver issues, database connectivity, and Docker misconfigurations.

When deploying Laravel applications to Alpine Linux environments, particularly within Docker containers, encountering a "SQLSTATE base table or view not found" error during php artisan migrate can be a frustrating roadblock. This guide delves deep into the root causes specific to Alpine's unique characteristics and provides a comprehensive, step-by-step resolution path for seasoned DevOps engineers and Systems Administrators.

Symptom & Error Signature

You attempt to run your Laravel database migrations, typically as part of a CI/CD pipeline, a Docker container build process, or directly on an Alpine-based server. The command php artisan migrate fails almost immediately, presenting an error similar to one of the following:

Typical MySQL Error:

$ php artisan migrate

   IlluminateDatabaseQueryException

  SQLSTATE[42S02]: Base table or view not found: 1146 Table 'your_database.migrations' doesn't exist (SQL: select `migration` from `migrations` order by `batch` asc, `migration` asc)

  at vendor/laravel/framework/src/Illuminate/Database/Connection.php:712
    708|         // If an exception occurs when attempting to run a query, we'll format the error
    709|         // message to be more readable so the developer is not confused by typical
    710|         // database errors. Then, we will re-throw the exception and pass it up the
    711|         // chain, allowing the developer to handle any database errors.
    712|         throw new QueryException(
    713|             $query, $this->prepareBindings($bindings), $e
    714|         );
    715|     }
    716|

      [previous exception]

    Exception trace:

    1   DoctrineDBALDriverPDOException::new(PDOException)
        vendor/doctrine/dbal/src/Driver/PDO/Exception.php:18
    2   DoctrineDBALDriverPDOException::fromDriverException(PDOException)
        vendor/doctrine/dbal/src/Driver/PDO/Connection.php:36
    3   DoctrineDBALDriverPDOConnection::query('select `migration` from `migrations` order by `batch` asc, `migration` asc')
        vendor/laravel/framework/src/Illuminate/Database/Connection.php:511
    4   IlluminateDatabaseConnection::IlluminateDatabase{closure}('select `migration` from `migrations` order by `batch` asc, `migration` asc')
        vendor/laravel/framework/src/Illuminate/Database/Connection.php:692
    5   IlluminateDatabaseConnection::runQueryCallback('select `migration` from `migrations` order by `batch` asc, `migration` asc', [], Closure)
        vendor/laravel/framework/src/Illuminate/Database/Connection.php:659
    6   IlluminateDatabaseConnection::run('select `migration` from `migrations` order by `batch` asc, `migration` asc', Closure)
        vendor/laravel/framework/src/Illuminate/Database/Connection.php:512
    7   IlluminateDatabaseConnection::select('select `migration` from `migrations` order by `batch` asc, `migration` asc', [], true)
        vendor/laravel/framework/src/Illuminate/Database/Schema/Builder.php:403
    8   IlluminateDatabaseSchemaBuilder::hasTable('migrations')
        vendor/laravel/framework/src/Illuminate/Database/Migrations/DatabaseMigrationRepository.php:201
    9   IlluminateDatabaseMigrations/DatabaseMigrationRepository::repositoryExists()
        vendor/laravel/framework/src/Illuminate/Database/Migrations/Migrator.php:793
    10  IlluminateDatabase/Migrations/Migrator::prepareDatabase()
        vendor/laravel/framework/src/Illuminate/Database/Migrations/Migrator.php:118
    11  IlluminateDatabase/Migrations/Migrator::run(array [], array [])
        vendor/laravel/framework/src/Illuminate/Database/Console/Migrations/MigrateCommand.php:87
    12  IlluminateDatabase/Console/Migrations/MigrateCommand::handle()
        vendor/laravel/framework/src/Illuminate/Container/Util.php:41
    13  IlluminateContainer/Util::IlluminateContainer{closure}(object(AppConsoleKernel))
        vendor/laravel/framework/src/Illuminate/Container/Util.php:34
    14  IlluminateContainer/Util::call([object(AppConsoleKernel)], 'handle')
        vendor/laravel/framework/src/Illuminate/Console/Command.php:184
    15  IlluminateConsole/Command::execute(object(SymfonyComponentConsoleInputArrayInput), object(SymfonyComponentConsoleOutputBufferedOutput))
        vendor/symfony/console/Command/Command.php:298
    16  SymfonyComponentConsole/Command/Command::run(object(SymfonyComponentConsoleInputArrayInput), object(SymfonyComponentConsoleOutputBufferedOutput))
        vendor/laravel/framework/src/Illuminate/Console/Command.php:150
    17  IlluminateConsole/Command::run(object(SymfonyComponentConsoleInputArrayInput), object(SymfonyComponentConsoleOutputBufferedOutput))
        vendor/symfony/console/Application.php:1040
    18  SymfonyComponentConsole/Application::doRunCommand(object(IlluminateDatabaseConsoleMigrationsMigrateCommand), object(SymfonyComponentConsoleInputArrayInput), object(SymfonyComponentConsoleOutputBufferedOutput))
        vendor/symfony/console/Application.php:301
    19  SymfonyComponentConsole/Application::doRun(object(SymfonyComponentConsoleInputArrayInput), object(SymfonyComponentConsoleOutputBufferedOutput))
        vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php:198
    20  IlluminateFoundation/Console/Kernel::handle(object(SymfonyComponentConsoleInputArrayInput), object(SymfonyComponentConsoleOutputBufferedOutput))
        artisan:37

Typical PostgreSQL Error:

$ php artisan migrate

   IlluminateDatabaseQueryException

  SQLSTATE[42P01]: Undefined table: 7 ERROR: relation "migrations" does not exist (SQL: select "migration" from "migrations" order by "batch" asc, "migration" asc)

  at vendor/laravel/framework/src/Illuminate/Database/Connection.php:712
    ... (similar stack trace as above)

This error indicates that Laravel attempted to query the migrations table, but the database reported it as non-existent.

Root Cause Analysis

The "SQLSTATE base table or view not found" error during php artisan migrate signifies that Laravel could not access the migrations table. This happens even on the first migration, where Laravel should create this table if it doesn't exist. This failure points to a deeper issue than simply a missing table. On Alpine Linux, given its unique musl libc implementation compared to glibc used by most other distributions, this often boils down to:

  1. Fundamental Database Connection Failure: Laravel's PDO driver (e.g., pdo_mysql, pdo_pgsql) is unable to establish a stable and functional connection to the database server. This could be due to:
    • Incorrect Database Credentials: Username, password, host, or port are wrong.
    • Network Connectivity Issues: The Alpine container/host cannot reach the database server (firewall, incorrect host/port mapping in Docker Compose, DNS resolution failure).
    • Missing or Incompatible Database Client Libraries: This is the most common and subtle culprit on Alpine. PHP's PDO extensions require underlying database client libraries (e.g., libpq for PostgreSQL, mariadb-connector-c or mysql-client-libs for MySQL/MariaDB). If these are not correctly installed, linked, or are incompatible with the database server version (e.g., old mariadb-connector-c client library connecting to MySQL 8+ using caching_sha2_password authentication), the PDO driver fails to initialize or connect properly, leading to the "base table or view not found" when Laravel attempts its first database interaction.
  2. Missing Database/Schema: The database specified in your .env file (e.g., DB_DATABASE=your_app_db) does not actually exist on the database server. Laravel cannot create the migrations table in a non-existent database.
  3. Database User Permissions: The database user specified in your .env file lacks the necessary CREATE TABLE, SELECT, INSERT, UPDATE, DELETE, and DROP permissions on the target database to manage migrations and application tables.

Given the "Alpine Linux" context, the focus for resolution often shifts to ensuring the correct and compatible PDO drivers and their underlying client libraries are installed within the Alpine environment, especially when using Docker.

Step-by-Step Resolution

Let's systematically troubleshoot and resolve this issue, starting with the most common problems and progressing to Alpine-specific nuances.

1. Verify Basic Database Connectivity & Credentials

Before diving into Alpine specifics, ensure the fundamental connection parameters are correct.

  1. Check .env Configuration: Ensure your APP_NAME's .env file has the correct database connection details.

    DB_CONNECTION=mysql # or pgsql
    DB_HOST=127.0.0.1 # or database_service_name for Docker Compose
    DB_PORT=3306      # or 5432 for Postgres
    DB_DATABASE=your_database_name
    DB_USERNAME=your_database_user
    DB_PASSWORD=your_database_password
    

    If using Docker Compose, DB_HOST should typically be the name of your database service (e.g., db or mysql_db). Do not use localhost or 127.0.0.1 unless the database server is truly running on the same host as the PHP container and is accessible directly.

  2. Test Network Connectivity: From within your Alpine container (or server), attempt to reach the database host and port.

    # If using MySQL/MariaDB
    apk add --no-cache mariadb-client # or mysql-client for pure MySQL
    mysql -h your_db_host -P your_db_port -u your_db_user -p
    
    # If using PostgreSQL
    apk add --no-cache postgresql-client
    psql -h your_db_host -p your_db_port -U your_db_user -d your_db_name
    

    Enter the password when prompted. If this connection fails, you have a network or basic authentication issue. Check firewalls, Docker network configurations, and database server logs.

2. Verify Database Existence and User Permissions

Ensure the database itself exists and your Laravel application's user has sufficient privileges.

  1. Check Database Existence: Connect to your database server as a privileged user (e.g., root for MySQL, postgres for PostgreSQL) and confirm that the database specified in DB_DATABASE actually exists.

    -- For MySQL/MariaDB
    SHOW DATABASES;
    CREATE DATABASE IF NOT EXISTS your_database_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    
    -- For PostgreSQL
    l
    CREATE DATABASE your_database_name;
    
  2. Verify User Permissions: Ensure the DB_USERNAME user has all necessary privileges on DB_DATABASE.

    -- For MySQL/MariaDB
    GRANT ALL PRIVILEGES ON your_database_name.* TO 'your_database_user'@'%' IDENTIFIED BY 'your_database_password';
    FLUSH PRIVILEGES;
    
    -- For PostgreSQL
    GRANT ALL PRIVILEGES ON DATABASE your_database_name TO your_database_user;
    -- You might also need to grant privileges on schemas within the database if you're not using the default 'public' schema
    -- GRANT ALL ON ALL TABLES IN SCHEMA public TO your_database_user;
    -- GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO your_database_user;
    -- ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO your_database_user;
    

    Granting ALL PRIVILEGES to '%' (any host) might be acceptable in development but is a security risk in production. Restrict the host to your application server's IP or hostname where possible (e.g., 'your_database_user'@'your_app_server_ip').

3. Correct Alpine-Specific PDO Driver & Client Library Installation (Most Common Fix)

This is often where Alpine setups diverge from glibc-based distributions. Ensure your Dockerfile or Alpine installation correctly installs PHP's PDO extensions and their underlying client libraries.

  1. Install PHP PDO Extensions and Client Libraries: Your Dockerfile for your PHP-FPM service is critical here. The php:*-fpm-alpine images provide docker-php-ext-install to simplify extension management.

    For MySQL/MariaDB: The common issue here relates to mariadb-connector-c-dev (which pdo_mysql often links against on Alpine) and potential incompatibilities with MySQL 8's default caching_sha2_password authentication.

    # Dockerfile snippet for a PHP-FPM service on Alpine (MySQL/MariaDB)
    FROM php:8.2-fpm-alpine # Use your desired PHP version
    
    # Install necessary system packages and PHP extensions
    RUN apk add --no-cache 
        curl 
        git 
        # For MySQL/MariaDB PDO driver and client utilities
        mariadb-client 
        mariadb-connector-c-dev 
        # Additional packages as needed, e.g., nginx if in the same container
        # nginx 
        && docker-php-ext-install pdo_mysql 
        && rm -rf /var/cache/apk/* # Clean up apk cache
    
    # Install Composer
    COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
    
    WORKDIR /var/www/html
    
    # Copy application files (or use a volume)
    # COPY . .
    
    # RUN composer install --no-dev --optimize-autoloader
    
    # EXPOSE 9000
    # CMD ["php-fpm"]
    

    If you're connecting to MySQL 8.0+ and encounter issues despite correct installation, the problem might be the default caching_sha2_password authentication method in MySQL 8. Older mariadb-connector-c client libraries (which pdo_mysql on Alpine typically uses) might not fully support it.

    Resolution for caching_sha2_password:

    • Option 1 (Recommended for compatibility): Create a user in MySQL that uses the older mysql_native_password authentication:
    CREATE USER 'your_database_user'@'%' IDENTIFIED WITH mysql_native_password BY 'your_database_password';
    GRANT ALL PRIVILEGES ON your_database_name.* TO 'your_database_user'@'%';
    FLUSH PRIVILEGES;
    
    • Option 2 (Less Secure for older clients): Change the default authentication plugin for the MySQL server to mysql_native_password (in my.cnf):
    [mysqld]
    default_authentication_plugin=mysql_native_password
    

    This requires a MySQL server restart.

    For PostgreSQL:

    # Dockerfile snippet for a PHP-FPM service on Alpine (PostgreSQL)
    FROM php:8.2-fpm-alpine # Use your desired PHP version
    
    # Install necessary system packages and PHP extensions
    RUN apk add --no-cache 
        curl 
        git 
        # For PostgreSQL PDO driver and client utilities
        postgresql-client 
        postgresql-dev 
        # Additional packages as needed
        && docker-php-ext-install pdo_pgsql 
        && rm -rf /var/cache/apk/* # Clean up apk cache
    
    # Install Composer
    COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
    
    WORKDIR /var/www/html
    # ... rest of your Dockerfile
    
  2. Rebuild Docker Image: After modifying your Dockerfile, you must rebuild your Docker image.

    docker-compose build --no-cache your_php_service_name
    docker-compose up -d
    

    The --no-cache flag ensures that the apk add and docker-php-ext-install commands are re-executed, picking up any changes or correcting previous faulty installations.

  3. Verify PHP Extensions are Enabled: Once the container is running, execute a command to verify the PDO extension is active.

    docker-compose exec your_php_service_name php -m | grep pdo_mysql # or pdo_pgsql
    

    You should see pdo_mysql (or pdo_pgsql) in the output. If not, the extension is not correctly installed or enabled.

4. Clear Laravel Configuration Cache

Sometimes, Laravel caches old configuration values. Clear them to ensure the application picks up any .env or configuration changes.

docker-compose exec your_php_service_name php artisan optimize:clear
docker-compose exec your_php_service_name php artisan config:clear
docker-compose exec your_php_service_name php artisan cache:clear

5. Retest Migrations

After performing the above steps, attempt to run your migrations again.

docker-compose exec your_php_service_name php artisan migrate

If all steps are followed correctly, your migrations should now run successfully, creating the migrations table and then proceeding with your application's schema.

👨‍💻

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn →

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

🛡️

Our Production Verification Guarantee

Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.