{"id":156,"date":"2026-06-06T09:03:00","date_gmt":"2026-06-06T09:03:00","guid":{"rendered":"https:\/\/blog.offshorekaka.in\/?p=156"},"modified":"2026-08-27T05:39:02","modified_gmt":"2026-08-27T05:39:02","slug":"install-wordpress-on-offshore-vps","status":"publish","type":"post","link":"https:\/\/offshorekaka.in\/blog\/install-wordpress-on-offshore-vps\/","title":{"rendered":"How to Install WordPress on an Offshore VPS"},"content":{"rendered":"<div class=\"ok-pro-panel\">\n<p><span class=\"ok-pro-kicker\">Installation Guides<\/span><\/p>\n<h2>WordPress on a bare VPS<\/h2>\n<p>A fresh <a href=\"https:\/\/offshorekaka.in\/offshore-vps-server\/\">offshore VPS<\/a> has nothing on it. No web server, no database, no PHP. Installing WordPress means building that stack first, then dropping WordPress on top of it.<\/p>\n<p>It is about twenty minutes of work. The parts people get wrong are file ownership and the PHP extensions, and both produce errors that look like something else entirely.<\/p>\n<div class=\"ok-pro-grid\">\n<div class=\"ok-pro-card\"><strong>Time<\/strong><span>20 minutes.<\/span><\/div>\n<div class=\"ok-pro-card\"><strong>Order<\/strong><span>Secure the server first, then install.<\/span><\/div>\n<div class=\"ok-pro-card\"><strong>Common trap<\/strong><span>Wrong ownership on wp-content.<\/span><\/div>\n<\/div>\n<\/div>\n<div class=\"ok-toc\"><strong>On this page<\/strong><a href=\"#first\">Before you install anything<\/a><a href=\"#stack\">The stack<\/a><a href=\"#db\">The database<\/a><a href=\"#files\">WordPress itself<\/a><a href=\"#vhost\">The vhost<\/a><a href=\"#ssl\">SSL and finishing up<\/a><a href=\"#faq\">FAQ<\/a><\/div>\n<h2 id=\"first\">Before you install anything<\/h2>\n<p>Harden the server first. It takes twenty minutes and the IP is already being scanned. Non-root user, firewall, key-only SSH, fail2ban. Our <a href=\"https:\/\/offshorekaka.in\/blog\/secure-offshore-vps-server-checklist\/\">VPS security checklist<\/a> is the short version.<\/p>\n<p>Point DNS at the server too, since you will want a certificate at the end and that needs the domain resolving here.<\/p>\n<h2 id=\"stack\">The stack<\/h2>\n<pre><code>apt update\napt install nginx mariadb-server php-fpm php-mysql php-curl php-gd php-xml php-mbstring php-zip php-intl unzip -y<\/code><\/pre>\n<p>Those PHP extensions are not optional padding. Leave out <code>php-gd<\/code> and image resizing silently stops working, so thumbnails never generate. Leave out <code>php-curl<\/code> and WordPress cannot reach the update servers, so the dashboard sits there saying it cannot check for updates and never explains why. Leave out <code>php-mbstring<\/code> and anything non-English breaks.<\/p>\n<p>Secure MySQL before going further:<\/p>\n<pre><code>mysql_secure_installation<\/code><\/pre>\n<p>This sets a root password, removes the anonymous accounts and drops the test database. All three exist by default on a fresh install.<\/p>\n<h2 id=\"db\">The database<\/h2>\n<pre><code>mysql -u root -p<\/code><\/pre>\n<pre><code>CREATE DATABASE wordpress CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\nCREATE USER 'wpuser'@'localhost' IDENTIFIED BY 'a-long-random-password';\nGRANT ALL PRIVILEGES ON wordpress.* TO 'wpuser'@'localhost';\nFLUSH PRIVILEGES;\nEXIT;<\/code><\/pre>\n<p>Use <code>utf8mb4<\/code>, not <code>utf8<\/code>. MySQL&#8217;s <code>utf8<\/code> is a three byte subset that cannot store emoji or a lot of non-Latin text, and you find out when a post silently truncates at the first emoji.<\/p>\n<p>Grant on <code>wordpress.*<\/code> only, and use <code>'localhost'<\/code> rather than <code>'%'<\/code>. A database user that can connect from anywhere on the internet is a mistake you make once.<\/p>\n<h2 id=\"files\">WordPress itself<\/h2>\n<pre><code>cd \/var\/www\nwget https:\/\/wordpress.org\/latest.tar.gz\ntar xzf latest.tar.gz\nmv wordpress yourdomain.com\nrm latest.tar.gz<\/code><\/pre>\n<p>Now ownership, which is where most installs go wrong:<\/p>\n<pre><code>chown -R www-data:www-data \/var\/www\/yourdomain.com\nfind \/var\/www\/yourdomain.com -type d -exec chmod 755 {} ;\nfind \/var\/www\/yourdomain.com -type f -exec chmod 644 {} ;<\/code><\/pre>\n<figure class=\"wp-block-image size-large ok-inline-visual\"><img decoding=\"async\" src=\"https:\/\/offshorekaka.in\/blog\/wp-content\/uploads\/2026\/08\/ok-file-permissions.webp\" alt=\"Terminal showing directory and file permissions and the find commands used to set them\" loading=\"lazy\" width=\"2296\" height=\"428\"><figcaption>755 on directories, 644 on files, owned by the web server user.<\/figcaption><\/figure>\n<p>The user is <code>www-data<\/code> on Ubuntu and Debian, <code>nginx<\/code> or <code>apache<\/code> on the RHEL family. Get it wrong and WordPress cannot write, so uploads fail and plugin installs ask for FTP credentials. That FTP prompt is almost always an ownership problem, not an FTP one.<\/p>\n<h2 id=\"vhost\">The vhost<\/h2>\n<p>Create <code>\/etc\/nginx\/sites-available\/yourdomain.com<\/code>:<\/p>\n<pre><code>server {\n    listen 80;\n    server_name yourdomain.com www.yourdomain.com;\n    root \/var\/www\/yourdomain.com;\n    index index.php index.html;\n\n    location \/ { try_files $uri $uri\/ \/index.php?$args; }\n\n    location ~ .php$ {\n        include snippets\/fastcgi-php.conf;\n        fastcgi_pass unix:\/run\/php\/php8.3-fpm.sock;\n    }\n\n    location ~* \/wp-content\/uploads\/.*.php$ { deny all; }\n}<\/code><\/pre>\n<p>The <code>try_files<\/code> line is what makes pretty permalinks work. Without it every URL except the homepage returns 404, and people spend an hour in the permalinks settings page looking for the problem.<\/p>\n<p>That last block stops PHP executing inside uploads. It costs nothing and it neutralises the most common backdoor, as covered in our <a href=\"https:\/\/offshorekaka.in\/blog\/malware-scan-wordpress-offshore-hosting\/\">malware guide<\/a>.<\/p>\n<pre><code>ln -s \/etc\/nginx\/sites-available\/yourdomain.com \/etc\/nginx\/sites-enabled\/\nnginx -t &amp;&amp; systemctl reload nginx<\/code><\/pre>\n<p>Always <code>nginx -t<\/code> before reloading. A syntax error caught here is a non-event; the same error found after a reload takes the site down.<\/p>\n<h2 id=\"ssl\">SSL and finishing up<\/h2>\n<pre><code>apt install certbot python3-certbot-nginx -y\ncertbot --nginx -d yourdomain.com -d www.yourdomain.com<\/code><\/pre>\n<p>Then open the site in a browser and run the WordPress installer. It asks for the database name, user and password you created above.<\/p>\n<p>Two things to do straight after:<\/p>\n<ul>\n<li><strong>Move WP cron to a real cron job.<\/strong> By default WordPress fires scheduled tasks on page loads, which makes random requests slow. Set <code>define('DISABLE_WP_CRON', true);<\/code> in <code>wp-config.php<\/code> and add <code>*\/5 * * * * curl -s https:\/\/yourdomain.com\/wp-cron.php &gt;\/dev\/null<\/code> to crontab.<\/li>\n<li><strong>Turn on caching.<\/strong> The single biggest speed difference. See our <a href=\"https:\/\/offshorekaka.in\/blog\/optimize-ttfb-on-offshore-hosting\/\">TTFB guide<\/a>.<\/li>\n<\/ul>\n<figure class=\"wp-block-image size-large ok-inline-visual\"><img decoding=\"async\" src=\"https:\/\/offshorekaka.in\/blog\/wp-content\/uploads\/2026\/08\/ok-gui-wp-permalinks.webp\" alt=\"WordPress Permalinks settings page\" loading=\"lazy\" width=\"2508\" height=\"2112\"><figcaption>If inner pages 404, the cause is the web server rewrite rule, not this page.<\/figcaption><\/figure>\n<h2 id=\"test\">How to test<\/h2>\n<ol>\n<li>Homepage loads over HTTPS.<\/li>\n<li>An inner post loads, which proves permalinks work.<\/li>\n<li>Upload an image in the dashboard. Success means ownership is right.<\/li>\n<li>Install a plugin. If it asks for FTP details, fix ownership.<\/li>\n<li><code>systemctl status nginx php8.3-fpm mariadb<\/code> all active.<\/li>\n<li>Reboot and confirm everything comes back.<\/li>\n<\/ol>\n<h2 id=\"trouble\">When something breaks<\/h2>\n<table>\n<thead>\n<tr>\n<th>What you see<\/th>\n<th>Why<\/th>\n<th>Fix<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Homepage works, inner pages 404<\/td>\n<td><code>try_files<\/code> missing from the vhost<\/td>\n<td>Add it and reload nginx<\/td>\n<\/tr>\n<tr>\n<td>WordPress asks for FTP credentials<\/td>\n<td>Files not owned by the web server user<\/td>\n<td><code>chown -R www-data:www-data<\/code><\/td>\n<\/tr>\n<tr>\n<td>Browser downloads the PHP file instead of running it<\/td>\n<td>PHP-FPM not wired into the vhost<\/td>\n<td>Check the fastcgi_pass socket path matches your PHP version<\/td>\n<\/tr>\n<tr>\n<td>Error establishing a database connection<\/td>\n<td>Wrong credentials, or MySQL not running<\/td>\n<td>Check <code>wp-config.php<\/code>, then <code>systemctl status mariadb<\/code><\/td>\n<\/tr>\n<tr>\n<td>Thumbnails never generate<\/td>\n<td><code>php-gd<\/code> not installed<\/td>\n<td>Install it and restart PHP-FPM<\/td>\n<\/tr>\n<tr>\n<td>White screen, no error<\/td>\n<td>PHP fatal error with display off<\/td>\n<td><code>tail -f \/var\/log\/nginx\/error.log<\/code> while loading the page<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 id=\"checklist\">Checklist<\/h2>\n<ul>\n<li>Server hardened before WordPress went on it.<\/li>\n<li>All PHP extensions installed, not just php-mysql.<\/li>\n<li><code>mysql_secure_installation<\/code> run.<\/li>\n<li>Database created as utf8mb4 with a localhost-only user.<\/li>\n<li>Ownership and permissions set correctly.<\/li>\n<li>vhost has <code>try_files<\/code> and blocks PHP in uploads.<\/li>\n<li>Certificate issued and HTTP redirecting.<\/li>\n<li>WP cron moved to system cron.<\/li>\n<li>Caching on.<\/li>\n<\/ul>\n<div class=\"ok-cta-box\">\n<p><strong>Want WordPress without building the stack?<\/strong><\/p>\n<p>OffshoreKaka shared hosting comes with LiteSpeed, cPanel and one click WordPress, so this whole page becomes a single button.<\/p>\n<p><a class=\"btn ok-cta-link\" href=\"https:\/\/offshorekaka.in\/offshore-web-hosting\/\">See the hosting plans<\/a><\/p>\n<\/div>\n<h2 id=\"faq\">FAQ<\/h2>\n<h3>Should I use a control panel instead?<\/h3>\n<p>If you are going to run more than one or two sites, yes. aaPanel is free and takes ten minutes, and it handles vhosts, certificates and backups for you. A plain stack is leaner and better if this is one application you know well.<\/p>\n<h3>nginx or Apache?<\/h3>\n<p>nginx uses less memory under load, which matters on a small VPS. Apache reads <code>.htaccess<\/code>, which many WordPress plugins expect and which makes some things easier. LiteSpeed gives you Apache compatibility with better performance, and our <a href=\"https:\/\/offshorekaka.in\/blog\/install-litespeed-on-offshore-vps\/\">LiteSpeed guide<\/a> covers installing it.<\/p>\n<h3>How much RAM does this need?<\/h3>\n<p>2GB is a comfortable starting point for one site. 1GB works with caching on and nothing else running, but MySQL alone wants a few hundred megabytes before it is useful. There is more detail in <a href=\"https:\/\/offshorekaka.in\/blog\/choose-offshore-vps-specs\/\">choosing VPS specs<\/a>.<\/p>\n<h3>Will installing it myself help my ranking?<\/h3>\n<p>No. Google cannot tell how the server was built. What it can tell is whether pages load quickly and stay available, and those come from caching and maintenance rather than from the install method.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You will prepare a VPS for WordPress with server stack, database, files and HTTPS.<\/p>\n","protected":false},"author":1,"featured_media":1025,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-156","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-installation-guides"],"_links":{"self":[{"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/posts\/156","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/comments?post=156"}],"version-history":[{"count":5,"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/posts\/156\/revisions"}],"predecessor-version":[{"id":957,"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/posts\/156\/revisions\/957"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/media\/1025"}],"wp:attachment":[{"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/media?parent=156"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/categories?post=156"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/tags?post=156"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}