> For the complete documentation index, see [llms.txt](https://docs.neevcloud.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.neevcloud.com/neevcloud-guide/stack-automation-library/web-and-applications/wordpress-on-single-instance.md).

# WordPress on Single Instance

Deploys a single Instance running WordPress with Nginx, PHP, and MySQL. Everything runs on one server. Suitable for blogs, small sites, or quick CMS testing.

#### Architecture:

* 1 Instance (NanoBoost — 2 vCPU, 4 GB RAM)
* 1 private network (10.200.0.0/24)
* 1 floating IP (public access)
* 40 GB boot volume
* Security group allows HTTP, HTTPS, SSH

#### What is automated:

* Instance creation with selected flavor and image
* Private network, subnet, and router
* Floating IP assigned to Instance
* Security group with ports 22, 80, 443
* Nginx + PHP-FPM + MySQL installed
* MySQL database and user created with random passwords
* WordPress downloaded, extracted, and configured
* wp-config.php populated with DB credentials and salts
* Credentials saved to `/root/.wp_credentials`

#### Before you launch:

Update these parameters in the template or at launch time:

| Parameter        | Default                | What to change                                                                 |
| ---------------- | ---------------------- | ------------------------------------------------------------------------------ |
| `key_name`       | `stack-test`           | **Required.** Replace with your SSH keypair name from **Compute - Key Pairs**. |
| `image`          | `Ubuntu 22.04 Updated` | Change only if you need a different OS image.                                  |
| `flavor`         | `NanoBoost`            | Minimum recommended. Upgrade to MegaFuse for heavier traffic.                  |
| `volume_size`    | `40`                   | Increase if you plan to host media-heavy content (in GB).                      |
| `public_network` | `Public`               | Do not change unless your cloud has a different external network name.         |

{% hint style="info" %}
**key\_name is the only parameter you must change before launching.** Everything else works with defaults.
{% endhint %}

#### WordPress Template

Save as `wordpress-single.yaml` and upload via Orchestration or Past Direct.

```
heat_template_version: 2021-04-16
description: >
  NeevCloud: WordPress on Single Instance with Nginx, MySQL, and PHP.
  Flavor minimum: NanoBoost (2 vCPU, 4 GB RAM).

parameters:
  image:
    type: string
    default: Ubuntu 22.04 Updated
  flavor:
    type: string
    default: NanoBoost
  volume_size:
    type: number
    description: Boot volume size in GB
    default: 40
  public_network:
    type: string
    default: Public
  key_name:
    type: string
    default: stack-test

resources:
  net:
    type: OS::Neutron::Net
    properties:
      name: wordpress-net

  subnet:
    type: OS::Neutron::Subnet
    properties:
      network: { get_resource: net }
      cidr: 10.200.0.0/24
      dns_nameservers: [8.8.8.8, 8.8.4.4]

  router:
    type: OS::Neutron::Router
    properties:
      external_gateway_info:
        network: { get_param: public_network }

  router_interface:
    type: OS::Neutron::RouterInterface
    properties:
      router: { get_resource: router }
      subnet: { get_resource: subnet }

  sg:
    type: OS::Neutron::SecurityGroup
    properties:
      name: wordpress-sg
      rules:
        - { direction: ingress, protocol: tcp, port_range_min: 22, port_range_max: 22, remote_ip_prefix: 0.0.0.0/0 }
        - { direction: ingress, protocol: tcp, port_range_min: 80, port_range_max: 80, remote_ip_prefix: 0.0.0.0/0 }
        - { direction: ingress, protocol: tcp, port_range_min: 443, port_range_max: 443, remote_ip_prefix: 0.0.0.0/0 }
        - { direction: ingress, protocol: icmp, remote_ip_prefix: 0.0.0.0/0 }

  server:
    type: OS::Nova::Server
    properties:
      name: wordpress-server
      flavor: { get_param: flavor }
      key_name: { get_param: key_name }
      networks:
        - network: { get_resource: net }
      security_groups:
        - { get_resource: sg }
      block_device_mapping_v2:
        - boot_index: 0
          delete_on_termination: true
          image: { get_param: image }
          volume_size: { get_param: volume_size }
      user_data_format: RAW
      user_data: |
        #!/bin/bash
        set -e

        DB_NAME="wordpress"
        DB_USER="wpuser"
        DB_PASS=$(openssl rand -hex 16)
        DB_ROOT_PASS=$(openssl rand -hex 16)

        export DEBIAN_FRONTEND=noninteractive

        apt-get update -y
        apt-get install -y nginx mysql-server php-fpm php-mysql php-xml php-mbstring php-curl php-zip php-gd php-intl unzip curl

        # Secure MySQL
        mysqladmin -u root password "${DB_ROOT_PASS}"
        mysql -u root -p"${DB_ROOT_PASS}" -e "CREATE DATABASE ${DB_NAME};"
        mysql -u root -p"${DB_ROOT_PASS}" -e "CREATE USER '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}';"
        mysql -u root -p"${DB_ROOT_PASS}" -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'localhost'; FLUSH PRIVILEGES;"

        # Download WordPress
        cd /tmp
        curl -sO https://wordpress.org/latest.tar.gz
        tar -xzf latest.tar.gz
        cp -a wordpress/. /var/www/html/
        chown -R www-data:www-data /var/www/html
        rm -f /var/www/html/index.nginx-debian.html

        # wp-config.php
        cp /var/www/html/wp-config-sample.php /var/www/html/wp-config.php
        sed -i "s/database_name_here/${DB_NAME}/" /var/www/html/wp-config.php
        sed -i "s/username_here/${DB_USER}/" /var/www/html/wp-config.php
        sed -i "s/password_here/${DB_PASS}/" /var/www/html/wp-config.php

        # Generate WordPress salts
        SALTS=$(curl -s https://api.wordpress.org/secret-key/1.1/salt/)
        # Remove existing placeholder salts and append real ones
        sed -i "/AUTH_KEY/d;/SECURE_AUTH_KEY/d;/LOGGED_IN_KEY/d;/NONCE_KEY/d;/AUTH_SALT/d;/SECURE_AUTH_SALT/d;/LOGGED_IN_SALT/d;/NONCE_SALT/d" /var/www/html/wp-config.php
        echo "${SALTS}" >> /var/www/html/wp-config.php

        # Determine PHP-FPM socket
        PHP_VER=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')
        FPM_SOCK="/run/php/php${PHP_VER}-fpm.sock"

        # Nginx vhost
        cat > /etc/nginx/sites-available/wordpress <<NGINX
        server {
            listen 80 default_server;
            root /var/www/html;
            index index.php index.html;
            server_name _;
            client_max_body_size 64M;

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

            location ~ \.php\$ {
                include snippets/fastcgi-php.conf;
                fastcgi_pass unix:${FPM_SOCK};
            }

            location ~ /\.ht {
                deny all;
            }
        }
        NGINX

        ln -sf /etc/nginx/sites-available/wordpress /etc/nginx/sites-enabled/default
        nginx -t && systemctl restart nginx
        systemctl enable nginx mysql php${PHP_VER}-fpm

        # Save credentials
        cat > /root/.wp_credentials <<EOF
        MySQL Root Password: ${DB_ROOT_PASS}
        WordPress DB: ${DB_NAME}
        WordPress DB User: ${DB_USER}
        WordPress DB Pass: ${DB_PASS}
        EOF
        chmod 600 /root/.wp_credentials

  fip:
    type: OS::Neutron::FloatingIP
    properties:
      floating_network: { get_param: public_network }

  fip_assoc:
    type: OS::Neutron::FloatingIPAssociation
    properties:
      floatingip_id: { get_resource: fip }
      port_id: { get_attr: [server, addresses, { get_resource: net }, 0, port] }

outputs:
  web_url:
    description: WordPress URL (complete setup via browser)
    value:
      str_replace:
        template: http://HOST
        params:
          HOST: { get_attr: [fip, floating_ip_address] }
  floating_ip:
    description: Public IP
    value: { get_attr: [fip, floating_ip_address] }
  credentials_note:
    description: Database credentials location
    value: "SSH into server and run: cat /root/.wp_credentials"
```

#### Access:

* Web: <http://floating-ip> (WordPress setup wizard)
* SSH: ssh ubuntu\@floating-ip

#### Deploy:

1. Go to [Orchestration](/neevcloud-products/orchestration-infrastructure-as-code/web-server-stack-template.md)
2. Upload wordpress-single.yaml
3. Select keypair, flavor, image
4. Launch stack
5. Open the floating IP in your browser and complete WordPress setup

{% hint style="info" %}
**Note:** First boot takes 3–5 minutes for all packages to install. If you see the default Nginx page, wait and refresh.
{% endhint %}
