What this solves
If you have ever looked for cheap node js hosting that does not fall apart the moment you need a second app, you know the trade: low price, but everything shares one runtime, one PHP version, one web server and one blast radius. A broken update on one site takes the others down with it.
Our web hosting is built the other way around. It runs on our own server in Romania, not on resold space. Each customer gets a separate Linux account with its own CPU, memory, process and disk limits, and each website runs in its own rootless Podman containers, without access to the server's local network or to other customers' accounts. A problem in one site does not reach your other sites or other customers.
This tutorial walks through ordering, adding a site, connecting a domain, deploying a Node.js or Python app, and the honest limits of the setup, so you can decide with full information.
Prerequisites
- A domain you control at a registrar, if you want your own name on the site. Without one, every site works on a preview address.
- Your application code, or a plan to start from the starter page.
- For Node.js and Python you need the Standard plan or higher; Start covers PHP and static sites only.
- A card for Stripe checkout. Prices are in EUR, VAT is calculated at checkout based on your country.
Step 1: Order a plan
- Open the hosting page and choose a plan and billing period (monthly or yearly). Yearly billing costs ten times the monthly price, and the price stays the same when you renew: there is no introductory price that goes up later.
- Add options if needed: +10 GB NVMe, +1 vCPU, +1 GB RAM or extended backup. You can change them later.
- Enter your e-mail address, accept the Terms, the Acceptable use policy and the Data processing agreement, and pay with Stripe.
Your hosting account is created automatically after payment, usually within a few minutes, and you receive an e-mail. The 30-day money-back guarantee starts with your first payment.
A quick orientation on plans: Start is 1 vCPU / 1 GB RAM / 10 GB NVMe with PHP and static sites. Standard is 2 / 2 GB / 25 GB and adds Node.js and Python, SSH with keys and Git. Pro adds Redis. Business adds your own container images. The current plans and options are on the hosting page.
Step 2: Add a website
Go to Websites → Add a website in Account → Hosting.
- Name: lowercase letters, digits and hyphens. It becomes the folder
www-data/<name>in your home folder. - Technology: PHP 8.1–8.5, Node.js 22 or 24, Python 3.12, or static.
- Web server (PHP only): Apache reads
.htaccessfiles and is the safe choice for WordPress and most PHP apps that ship rewrite rules there. nginx is lighter and faster for static files but ignores.htaccess. You choose per site and can mix both in one account. - Domain (optional): your own domain or a subdomain of a domain in your account; the subdomain's DNS record is created automatically. Without a domain the site works right away on its preview address,
<site>-<user>.hosting.medialab.works(HTTP only until you move the domain). - Web root (PHP and static): the folder inside
www-data/<site>served to visitors,publicby default;.means the site folder itself.
When the site is created, its folder gets a starter page showing that the domain reaches the server, where the site is hosted and which PHP version runs. Replace it with your own files.
Where files go:
| Technology | Folder |
|---|---|
| PHP, static | www-data/<site>/public (or the web root you chose) |
| Node.js, Python | www-data/<site>/app |
Step 3: Connect your domain and get DNS and mail for free
When you add a domain, we create three things for you: a DNS zone in Cloudflare in our account with the web records and the mail records (MX, SPF, DKIM, DMARC, autoconfig), the mail domain on our mail server with its DKIM key, and the website on the server with an SSL certificate as soon as the domain points to us.
What you do: at your registrar, replace the nameservers with the two shown in Account → Hosting → Domains. The change usually takes from a few minutes to a few hours, sometimes up to 48 hours. The domain page shows each step; when the nameservers are seen, the zone becomes active and the certificate is issued automatically. Check DNS and Issue certificate let you retry immediately.
Keeping your current DNS provider? Point an A record to the IP address shown in the account instead, and add the mail records yourself if you want e-mail on the domain; the DNS records table in the account lists them. Without our nameservers, the DNS records are your responsibility.
Step 4: Deploy a Node.js or Python app
Upload your app into www-data/<site>/app. Two rules decide whether it works:
- It must listen on the port in the
PORTenvironment variable (8080 inside the container) on all interfaces (0.0.0.0), not only onlocalhost. The web server and the certificate sit in front of it; you do not open any port yourself. - Startup order matters. For Node.js: an executable
start.shinapp/, otherwisenpm startfrompackage.json(dependencies are installed withnpm ci --omit=devwhennode_modulesis missing), otherwiseserver.js,index.js,app.jsormain.js. For Python: an executablestart.sh, otherwiseasgi.py(served by uvicorn workers in gunicorn), otherwiseapp.pywith anappobject orwsgi.pywithapplication(gunicorn).requirements.txtis installed into a virtual environment and reinstalled at start when the file has changed.
After uploading a new version, press Restart next to the site. Until an app is found, the site shows a placeholder page that says what is missing.
For PHP apps the flow is familiar: create a MariaDB database in Databases (its full name becomes <user>_wp), upload the project into www-data/<site>, then press Detect in Settings next to the site. We recognize Laravel, Symfony, WordPress, Drupal, Nextcloud, WHMCS and others, and suggest the web root. Files outside the web root (vendor/, storage/, .env, the Nextcloud data, WHMCS's attachments, downloads and templates_c) stay reachable for the app but not from the browser. For WordPress, DB_HOST is mlw-db:3390. The password of your database user is shown once, when you create your first MariaDB database: save it.
Step 5: Mail from your site on port 587
Outgoing mail from your sites goes through our mail server with a mailbox, on port 587. Direct sending on port 25 is blocked, which keeps the server's address off spam lists. Mailboxes, webmail, IMAP and SMTP are part of every plan.
Step 6: Backups you restore yourself, and their honest limits
Backups run every night with restic, encrypted, on a separate disk in the same server. You can restore files and databases yourself from your account, take on-demand backups, and download a site or a database from any backup. Retention depends on the plan: 7 days on Start, 14 on Standard, 14 daily plus 4 weekly on Pro, 30 daily plus 12 monthly on Business. Extended backup keeps them as long as on the next plan. Restore by our team is available too; the first one each month is free.
Here is the honest part: backups are kept on the same server, on a separate disk, not in a second location. They protect you from mistakes, a broken update or a hacked site, but not from the loss of the whole server. Keep your own copy of anything important; you can download your files and databases at any time. There is also one server: availability is backed by our SLA (99.9 % per month, with credits on the next invoice), but there is no automatic failover to a second machine. And "unlimited" on Business means fair use: the CPU, RAM and disk of the plan still apply, and technical caps exist only to stop abuse (see the Terms).
Tips and troubleshooting
- Site shows the placeholder page: your app was not found or is not listening correctly. Check that it binds to
0.0.0.0:$PORT, then press Restart. - Read the logs first: every site logs to
logs/<site>/(nginx/orapache/withaccess.loganderror.log, plusphp/,node/orpython/for the app's output). The same logs appear in Settings → Logs. - Moving a WordPress site: upload the archive, extract it in Files, fix
wp-config.php, then Detect the app. Apache is the safer default for rewrite rules. - Large uploads: the file manager accepts files of any size your plan allows, sent in chunks, and extracts
.zip,.tar.gz,.tar.bz2or.tar.xz. Downloading a whole folder as.zipis prepared in the background and stays under Operations and downloads for 24 hours. - Stuck on DNS: wait for propagation, then use Check DNS and Issue certificate to retry without opening a ticket.
- Need a database in the browser: Adminer opens with one click for MariaDB and PostgreSQL, with
.sqlexport and import.
More answers are in the knowledge base and the FAQ; the step-by-step from ordering to your first mailbox is in the getting started guide.
Conclusion
Isolation per site is not a marketing word here: it is a separate Linux account with its own limits and rootless Podman containers per website, on hardware we run ourselves in Romania. You get Apache or nginx per site, PHP 8.1–8.5, Node.js 22/24 and Python 3.12 from Standard, automatic Cloudflare DNS with mail records, free SSL, and a renewal price that does not jump. The limits are stated plainly: backups live on the same server, and it is one machine. If that matches how you run your own servers, the hosting page has the plans, and you can contact us with anything the guide does not cover.