Skip to content
megapromotingLet's talk

Expertise · Infrastructure and operations

We deploy the application on a server we control, with reversible publishing, verified backups and a place where it is visible when something has gone down.

Hosting, deployment, monitoring and continuity for web applications and database-backed platforms. We work on infrastructure the client can claim: virtual machines with European providers, nginx, supervised processes, containers, self-hosted database. Includes the answer to the question “where exactly is my data stored”.

Already builtWe operate several proprietary systems in this way, and the configurations are in repositories, not only on servers. This site runs on nginx with Next.js 16 under PM2, at OVHcloud Bucharest. The assistant platform runs on Microsoft Azure, Poland Central, with MySQL, Redis and RabbitMQ in containers connected to the local interface. A proprietary civic platform uses systemd units with publication through atomic symbolic-link switch-over, a verified daily backup and a separate retention task that runs only if that backup succeeded. An internal board is published through our own runner, with GitHub actions pinned to the full fingerprint and with automatic rollback to the previous version if the health check fails after publication. The reservation that must be stated: the degree of automation differs from system to system. Publication of this site is still done manually, and database migrations are applied by hand, deliberately. We do not sell an automated chain that we do not have everywhere.

An application that works on a laptop and one that works in production differ in things that are not visible in the interface: what happens when the process dies at three in the morning, what happens when a deployment goes badly, where the logs go, who finds out first that something has gone down and from where the database is recovered if the disk disappears. This service handles exactly that part.

The basic choice we propose is the infrastructure the client can claim: a virtual machine at a European provider, nginx in front, processes under a supervisor, containers where it makes sense, self-hosted database where independence matters more than convenience. Not because managed platforms are bad — but because one day you want to be able to take everything and leave, and if that was not considered from the start, it can no longer be done cheaply.

The question of data sovereignty has only one honest answer: a verified one. The location is confirmed by querying the virtual machine metadata service and the address registry, not by reading the provider’s marketing page. The difference is not theoretical — for a transfer within the European Economic Area, the transfer chapter of Law 195/2024 simply does not apply. A migration done by us moved a platform from a database hosted in the United States to a self-hosted stack in the European Union, precisely for this reason.

What we do not promise: that everything is automated. We apply schema migrations by hand, deliberately, because a migration applied automatically in production by a chain that does not know what is in the table is the usual way data is lost. Code publishing is automated; changing the database structure remains a human decision, with a fresh backup behind it.

What it covers

The work, by component

Reversible publishing, with verification after, not only before

Publishing does not end when the files have reached the server, but when a real request proves that the new version is the one being served. In one of the implementations, the script synchronises the directory, keeps the previous version alongside it, then extracts the package name from the delivered page and checks on the public address whether exactly that file is being served; if not, it reverts to the previous version on its own. This is not theoretical: the mechanism caught a package left old in the runner directory and cancelled a release that would otherwise have seemed successful.

Atomic version switching, with the process under systemd

The alternative to ‘stop, copy over, start’ is a version directory plus a symbolic link switched in a single operation. The systemd unit always points to the stable path, and rolling back means switching the link back. The unit has a restart-on-failure policy with a short pause, maximum stop time, and system restrictions: no privilege escalation, protected file system, inaccessible home directories, with an explicit list of paths it is allowed to write to.

nginx in front, with limits written for the real case

TLS termination with automatic certificate renewal, HSTS with a one-year duration and subdomain inclusion, compression with a minimum size threshold, static resources with long expiry and immutable marking, and the application bound to the local interface so it cannot be reached directly from the internet. Rate limits are set by traffic type, not globally. A lesson paid for is worth stating: under HTTP/2, the connection limit counts streams, not connections — at a small value, a single page load rejects its own fonts and scripts with 429.

Back-up copy that is verified, not just run

Scheduled daily task, with start at the next boot if the machine was off at the scheduled time and with random delay so everything does not start in the same second. The database export is compressed, and if the resulting file is empty, the execution is treated as a failure — an export that ends ‘successfully’ and produces zero bytes is the usual way to discover, six months later, that there is no copy. The files in the object store are synchronised separately. A 100% local copy does not survive disk loss, so the copy also goes outside the machine, to a storage account in the European Union with geographic replication, versioning, reversible deletion and automatic expiry — with access through managed identity, so there is no storage key on disk.

Scheduled deletion runs only after a successful copy

The retention task explicitly depends on the copy task and starts after it. A clean-up without a fresh copy is the only form of deletion from which there is no return. Unlike copying, deletion does not recover missed executions: if the machine was off, it is not deleted twice the next day. Each run writes a row to an executions table, and before the first real activation the task runs dry for at least a week and the log is read.

Alarms that do not lie and do not spam

A scheduled task that returns 200 does not mean a task that worked. Our execution wrapper reads the response body and looks in it for partial failures, then sends an alert with rate limiting, so a service down all day does not produce ninety-six identical messages. The reason it exists: a daily report was dead for eleven days in a row, and the only trace was an empty cell in a table nobody was looking at. The scheduled task had triggered all eleven times.

Publishing chain that cannot be hijacked upstream

The actions in the integration flow are pinned to the full commit hash, not to a tag. In March 2026, a popular action was redirected from tags `v1`…`v45` to malicious code and reached over 23,000 repositories in 24 hours; a hash does not move. The flow does not keep credentials in the working copy, and publishing does not go through a token: it goes through a self-hosted runner, on the target machine, with rights granted point by point. Thus no server access key sits in the code platform.

Containers with limits, health checks and logs that do not fill the disk

Each service has a restart policy, its own health check (database readiness, an application status endpoint), CPU and memory limits where the load requires them, and log rotation at container engine level, with maximum size and number of files. Database ports are bound to the local interface, never public. Required variables are declared so that the container refuses to start if they are missing, instead of starting with a dangerous default value.

Self-hosted database, when independence matters

Full stack in containers — database, authentication, REST interface, real-time channel, file storage, access gateway — with the extensions explicitly enabled and with memory and connection parameters suited to the machine, not left at the default values. Migrations are versioned files in the repository and are applied manually, with restart of the component that keeps its schema in memory. The reason for this choice is simple: a platform managed from outside the European Union can be technically excellent and yet legally unsuitable.

What it looks like

The path, step by step.

01

We inventory what exists and what can be lost

What is actually running on the machine, under which supervisor, with which versions, with which ports exposed; what backups exist and whether any have ever been restored; what secrets have ended up in repositories; what the difference is between the configuration in the repository and the file on the server. The last point almost always produces surprises — we ourselves have documented a case where the file on the server had been edited directly, outside the repository, and had left a timestamped backup copy beside it. We deliver the inventory and the list of single points of failure.

02

We put in place the foundation: server, nginx, processes, hardening

Firewall that rejects incoming traffic by default and opens only what is needed, protection against repeated authentication attempts, properly sized swap space, nginx with TLS and automatic renewal, the application bound to the local interface, the process supervisor configured to start at boot. We deliver the configurations in the repository, not only on the machine, so the next person does not have to reconstruct them from memory.

03

We make publication repeatable and reversible

Publication script with the previous version kept, health check after publication and automatic rollback on failure. Where there is a team and continuous integration, a self-hosted runner is added on the target machine, with actions pinned to a fingerprint and without credentials in the working copy. We deliver the written procedure and a rollback executed in front of the client — an untested rollback is not a rollback.

04

We put in place backups, retention and alerts

Daily backup with verification that the file is not empty, off-machine backup in a European Union region, retention task that runs only after a successful backup and leaves a trace, plus rate-limited alerts that read the response content, not just the status code. We deliver a test restore actually performed and its log.

05

We hand over the keys and write down what remains undone

Access in own account, operating documentation, list of scheduled tasks and — mandatory — the honest list of things that were not automated and why. For us, two almost always remain on the list: schema migrations, applied by hand deliberately, and the publication of this site, which is still manual.

Ce vede utilizatorulThe applicationNetwork and securityHosting and dataEVERY LAYER, CHOSEN AND EXPLAINED
The layers of an installation, from bottom to top: the virtual machine in a confirmed European region, the container engine with log rotation, the database with the verified daily copy and the off-machine copy, the processes under a supervisor with restart on failure, nginx with TLS and rate limits — and alongside, the publication chain with the previous version kept and automatic rollback.

The data

What we touch, where it lives, and how long it stays

The questions anyone with a data protection officer would ask — asked here before they do.

Where the data actually resides and how it is checked
The location is confirmed from the machine, not from documentation: the provider’s metadata service states the real region, and the address register states the country. Our systems are hosted on OVHcloud, Bucharest, Romania and on Microsoft Azure, Poland Central — both in the European Economic Area. The backups of the civic platform are in a northern region of the Union, with replication to a second region also in the Union. These lines replaced a longer list of locations we could not confirm.
What we actually see during the work
Configuration, logs, database schema and process state. Personal data content is not part of the work; where debugging still requires an example, a case created for that is used. Administrative access is granted for the duration of the intervention, by name, and is withdrawn at the end — and the withdrawal is verified, not assumed.
Logs and how long they live
The web logs of this site are rotated daily, with fourteen copies kept, with read rights restricted to the administration group. At container level, rotation is handled by the container engine, with fixed size and number of files, so that a chatty service does not fill the disk and stop the database — the most banal way to take production down.
Secrets do not live in the repository
Sensitive values come from environment files with restricted rights, read by the system unit, not written in the unit — because the definition of a unit and the system journal are readable by more people than is assumed. In the publishing chain, the production environment file does not pass through the code platform: it is copied locally on the machine and deleted immediately after the build. When we take over an existing infrastructure, the first pass is always an inventory of the secrets that have reached the stores, with a rotation plan.
Continuity: what happens when we leave
Handover means self-owned access to the infrastructure provider, the repository with all server configurations, the publishing and rollback procedure, the backup restoration procedure — tested, not just written — and the list of scheduled tasks with what each does. Protection against accidental deletion is applied to the production resource group from the start.

A case

A daily report that had not run for eleven days, in a system where everything returned 200

The situation

In an internal proprietary platform, several automated routines produce daily reports and clean data. The tasks were started from within the application, with counters in process. Nothing indicated any problem: the service was running, the address responded, the journals contained no errors.

What we built

The daily report had been missing for eleven days, and the only trace was a gap in a table that no one was looking at. Two distinct faults emerged. First: a counter started inside the application resets on every service restart, so a routine with an interval longer than the interval between publications never triggers. Second, more insidious: the routine had been triggered correctly all eleven times and had returned code 200 each time — but the response body contained partial failures that no one was reading.

What came out

Scheduling was moved outside the application, into a system scheduler that does not care about restarts. Execution was wrapped in a script that writes one log line per run, scans the response body for failure keys and sends an alert to a messaging channel — with rate limiting, so that a dependency down all day does not produce ninety-six identical messages. A 200 response with internal failures now has its own status, distinct from success.

What the case does not say

None of this was an infrastructure problem in the classic sense: the server was running, the disk had space, the process was alive. That is exactly why it took eleven days. The most expensive operational faults are not outages — those are visible — but things that report success without doing anything. The health check that matters does not ask “are you up?”, but “did you do what you were supposed to do, as many times as you were supposed to do it?”.

Questions

What people ask us before they call

Where will our data actually be stored?

Where you choose, and we verify that it is really there. Our systems are on OVHcloud, Bucharest and on Microsoft Azure, Poland Central, both in the European Economic Area, and the location was confirmed by querying the virtual machine metadata service and the address register — not by reading the provider’s documentation. The verification matters: for a transfer within the European Economic Area, the transfer chapter of Law no. 195/2024 does not apply and no special authorisations are required. Outside it, a file of guarantees appears.

Why a self-hosted server and not a managed platform?

Not always. A managed platform is the right choice when the team is small, traffic is irregular and nothing in the stack has residency requirements. A self-hosted server becomes the better argument in three situations: when data must remain in a specific jurisdiction, when cost becomes unpredictable at scale, and when you want to be able to take everything and leave. We have also done the reverse migration for a self-hosted system: from a database hosted in the United States to a self-hosted stack in the European Union, with eight containers — database, authentication, REST interface, real-time, storage, metadata, dashboard and access gateway.

What happens if a publication goes wrong?

It is rolled back, and preferably by itself. The previous version remains on disk alongside the new one, and after publication a check requests the real page and confirms that the file served is the one just built; if not, the script rolls back automatically. Where we use atomic symbolic link switching, rollback is a single operation. This is not a brochure description — the mechanism has already caught a publication with an old package left in the runner’s working directory and cancelled it.

Do you make backups? How often and do you test them?

Daily where we built it — and we stress this, because an automatic backup does not appear by itself when you move an application to a server. The backup runs at a fixed time, is restored on the next boot if the machine was off, and if the export is empty the execution is treated as a failure. The backup also leaves the machine, to a region in the European Union, with replication, versioning, reversible deletion and automatic expiry. The restore test is part of delivery: a backup that has never been restored is an assumption, not a backup.

Who finds out first when something goes down?

It depends on what we built, and it is worth saying without embellishment. Container-level health checks and application status endpoints exist; the alarm to a messaging channel, with rate limiting, exists where we built it. A status check point that no one queries is not monitoring — it is a page. If you want real monitoring, it is a separate stage, with an external observer that queries periodically and an alert recipient who is allowed to be woken up.

Do you apply database migrations automatically on every deployment?

No, and that is a deliberate decision, not an oversight. The deployment chain builds and copies code; that is all. Schema changes are versioned files in the repository, applied manually, with a fresh backup behind them, and components that keep their schema in memory are restarted afterwards. An automatically applied migration in production by a process that does not know what is in the table is the usual way to lose data irreversibly.

How do you prevent your deployment chain from becoming an attack path?

By three rules. External actions are pinned to the full commit fingerprint, not to a tag — in March 2026 a widely used action was redirected from its tags to malicious code and reached more than 23,000 repositories in 24 hours. The working copy does not keep credentials. Deployment is not done with an access key stored in the code platform, but through its own runner that executes on the target machine, with rights granted point by point. We recognise that not all our older projects still follow all three; migrating them is a task in itself.

Do you take over infrastructure built by someone else?

Yes, and the first pass is always an inventory, not a change. What actually runs, under which supervisor, with which versions, which ports are exposed, which backups exist, whether any has ever been restored, what secrets have ended up in repositories, and to what extent the configuration in the repository still resembles the file on the server. The last check almost always turns up something: we have documented, in a project of our own, a configuration file edited directly in production, which left a backup copy with a timestamp beside it.

What happens if we want to work with someone else?

You leave entirely. The infrastructure sits on your accounts with the provider from the start, and the handover includes the repository with all server configurations, the deployment and rollback procedure, the tested restore procedure, and the list of scheduled tasks with what each one does. Protection against accidental deletion of the production resource group is set up from installation. If a provider conditions your departure on rewriting the infrastructure, that was the problem, not the technology.

What the statements above rest on (30 sources)
  1. https://www.megapromoting.com responds 200 on nginx + Next.jshttps://www.megapromoting.com/servicii · 2026-09-06

29 of them are code and files from our repositories. We don't publish their name or line: together, on a single page, they would describe too precisely how systems that aren't only ours are built. We go through them with you, in the repository, on request — verification stays possible, it just happens in a conversation.

What would you want to work better?

Tell us about your process. Together we decide what is worth building, what we can connect, and how we check the result.

Let's talk