Cloud Basics for People Who Write Code on Their Own Machine
Moving software from a laptop to a server introduces ideas most tutorials skip. A few core concepts explain most of the trouble.

On this page
Code that runs on a laptop depends on things the laptop supplies without being asked: a file system with the expected paths, libraries installed in the expected places, environment variables set by a shell profile, and a network the machine trusts. A server supplies none of this by default. Moving a program to the cloud is mostly a matter of making those assumptions explicit. Most deployment trouble comes from one that was never written down.
Compute, storage and networking in plain terms
Compute is the machine, or container, that runs the process. Storage is where data lives, and the important question is whether it survives when the process stops. Some disks attached to a running machine are temporary, and anything written to them disappears when the machine is replaced. Durable storage is a separate service that is addressed explicitly. Networking decides which addresses and ports can be reached, and by whom. A service that answers on a laptop may be unreachable from outside because nothing opened the port.
Most deployment bugs trace back to one of those three assumptions. The program writes a file the next run cannot find, reads a setting that exists only on the laptop, or listens on an address nothing outside the machine can reach.
Other quiet differences include time zones, character encodings and the locale settings that change how dates and numbers are formatted. A server often runs in a different time zone from the laptop, and a timestamp written without a zone can be read differently. State the zone in the code and in the logs.
Environment variables and secrets
Configuration that differs between environments, such as a database address or a feature flag, belongs outside the code, usually in environment variables. Secrets such as access keys belong in a dedicated store or in variables injected at run time, never in the repository. Anything committed is copied to every clone and usually stays in the history even after the file is deleted.
If a key has been committed, treat it as exposed. Revoke it, create a new one, and remove it from the history if the project requires that. Deleting the file alone does not undo the exposure.
Deploying a small app
A dependable deployment is usually uneventful, and it follows a short sequence.
- Pin dependencies in a file that records exact versions, so the same build can be recreated.
- Run the app locally with the same start command the server will use, not a development shortcut.
- Supply configuration through variables, and confirm the program fails clearly when one is missing.
- Deploy, then check a health endpoint or a simple request to confirm the app answers.
- Read the logs from the first run before declaring success.
Each step addresses a specific failure. Unpinned dependencies produce a build that differs from the one tested. A development start command hides the production configuration. A silent failure on a missing variable shows up later as a confusing error.
File names are a common surprise. Some systems treat a file called Config as different from one called config, and others do not. A program that loads a file with the wrong capitalization will work on a laptop and fail on a server. Check names against the repository rather than against the local file browser.
Logs as the only window
On a server, nobody watches the terminal. Logs are the record of what the program did, and often the only way to learn why a request failed. Write lines that state the event, a relevant identifier and the outcome. "Request failed" says almost nothing. A line that includes the request identifier, the step that failed and the error type makes it possible to follow one case through the system.
Be careful about what enters the logs. Secrets and personal data should be excluded or masked. Logging everything is expensive to store and search, and it buries the signal. Start with decisions and failures at the edges of the system, and add detail where a real problem has appeared.
Give each request an identifier that follows it across every service it touches, so one failure can be traced end to end. Adding this early costs little. Adding it after the first incident usually means the incident is already hard to reconstruct.
Cost awareness from the start
Cloud resources often cost money when idle, not only when busy. Storage accumulates, data sent out of a system is metered, and a forgotten instance keeps running. Learn the categories the provider bills for, set a spending alert before deploying anything, and check the bill after the first few days. Prices change, so look them up at the time rather than relying on memory. Set the spending alert on the account as a whole, not only on one resource, since the cost of a forgotten experiment is easy to miss in a single line item.
The trade-off is that the cheapest settings can be slow or limited. Choose them for experiments, and choose more carefully for anything other people depend on.
Practice by moving one script off the laptop
Take a small script written on a personal machine. List every assumption it makes: the files it reads, the variables it expects, the network calls it makes, and the tool versions it needs. Run it in a clean container or a fresh virtual environment, and fix each failure by making the assumption explicit rather than copying something from the original machine. If you have an account on a cloud platform, deploy it, check the logs for one run, and delete the resources afterward. The test that matters is whether someone else can run it from the repository alone, following only the written steps.

